- in CNCJob UI Autolevelling - updated the UI with controls for probing GCode parameters and added signals and slots for the UI

- in CNCJob UI Autolevelling - added a mini gcode sender for the GRBL to be able to send the probing GCode and get the height map (I may make a small and light app for that so it does not need to have FlatCAM on the GCode sender PC)
- in CNCJob UI Autolevelling finished the probing GCode generation for MACH/LinuxCNC controllers; this GCode can also be viewed
This commit is contained in:
Marius Stanciu 2020-08-16 23:05:31 +03:00
parent cc7c525be1
commit fd895e3c21
4 changed files with 545 additions and 47 deletions

View File

@ -7,6 +7,12 @@ CHANGELOG for FlatCAM beta
=================================================
16.08.2020
- in CNCJob UI Autolevelling - updated the UI with controls for probing GCode parameters and added signals and slots for the UI
- in CNCJob UI Autolevelling - added a mini gcode sender for the GRBL to be able to send the probing GCode and get the height map (I may make a small and light app for that so it does not need to have FlatCAM on the GCode sender PC)
- in CNCJob UI Autolevelling finished the probing GCode generation for MACH/LinuxCNC controllers; this GCode can also be viewed
14.08.2020
- in CNCJob UI worked on the UI for the Autolevelling

View File

@ -365,7 +365,7 @@ class AppTextEditor(QtWidgets.QWidget):
def handleCopyAll(self):
text = self.code_editor.toPlainText()
self.app.clipboard.setText(text)
self.app.inform.emit(_("Code Editor content copied to clipboard ..."))
self.app.inform.emit(_("Content copied to clipboard ..."))
# def closeEvent(self, QCloseEvent):
# super().closeEvent(QCloseEvent)

View File

@ -1917,11 +1917,11 @@ class CNCObjectUI(ObjectUI):
grid0.setColumnStretch(1, 1)
self.al_box.addLayout(grid0)
al_title = FCLabel('<b>%s</b>' % _("Test Points Table"))
al_title = FCLabel('<b>%s</b>' % _("Probe Points Table"))
al_title.setToolTip(_("Generate GCode that will obtain the height map"))
self.show_al_table = FCCheckBox(_("Show"))
self.show_al_table.setToolTip(_("Toggle the display of the Test Points table."))
self.show_al_table.setToolTip(_("Toggle the display of the Probe Points table."))
self.show_al_table.setChecked(True)
hor_lay = QtWidgets.QHBoxLayout()
@ -1931,16 +1931,16 @@ class CNCObjectUI(ObjectUI):
grid0.addLayout(hor_lay, 0, 0, 1, 2)
self.al_testpoints_table = FCTable()
self.al_testpoints_table.setColumnCount(3)
self.al_testpoints_table.setColumnWidth(0, 20)
self.al_testpoints_table.setHorizontalHeaderLabels(['#', _('X-Y Coordinates'), _('Height')])
self.al_probe_points_table = FCTable()
self.al_probe_points_table.setColumnCount(3)
self.al_probe_points_table.setColumnWidth(0, 20)
self.al_probe_points_table.setHorizontalHeaderLabels(['#', _('X-Y Coordinates'), _('Height')])
grid0.addWidget(self.al_testpoints_table, 1, 0, 1, 2)
grid0.addWidget(self.al_probe_points_table, 1, 0, 1, 2)
self.voronoi_cb = FCCheckBox(_("Show Voronoi diagram"))
self.voronoi_cb.setToolTip(
_("Display Voronoi diagram if there are test points in the table.")
_("Display Voronoi diagram if there are probe points in the table.")
)
grid0.addWidget(self.voronoi_cb, 3, 0, 1, 2)
@ -1951,8 +1951,8 @@ class CNCObjectUI(ObjectUI):
al_mode_lbl = FCLabel('<b>%s</b>:' % _("Mode"))
al_mode_lbl.setToolTip(_("Choose a mode for height map generation.\n"
"- Manual: will pick a selection of test points by clicking on canvas\n"
"- Grid: will automatically generate a grid of test points"))
"- Manual: will pick a selection of probe points by clicking on canvas\n"
"- Grid: will automatically generate a grid of probe points"))
self.al_mode_radio = RadioSet(
[
@ -1982,7 +1982,7 @@ class CNCObjectUI(ObjectUI):
grid0.addWidget(self.al_rows_label, 11, 0)
grid0.addWidget(self.al_rows_entry, 11, 1)
self.al_add_button = FCButton(_("Add test points"))
self.al_add_button = FCButton(_("Add Probe Points"))
grid0.addWidget(self.al_add_button, 13, 0, 1, 2)
separator_line = QtWidgets.QFrame()
@ -1990,27 +1990,232 @@ class CNCObjectUI(ObjectUI):
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 14, 0, 1, 2)
self.al_controller_label = FCLabel('%s:' % _("Controller"))
# #############################################################################################################
# ############### Probe GCode Generation ######################################################################
# #############################################################################################################
self.probe_gc_label = FCLabel('<b>%s</b>:' % _("Probe GCode Generation"))
self.probe_gc_label.setToolTip(
_("Will create a GCode which will be sent to the controller,\n"
"either through a file or directly, with the intent to get the height map\n"
"that is to modify the original GCode to level the cutting height.")
)
grid0.addWidget(self.probe_gc_label, 16, 0, 1, 2)
# Travel Z Probe
self.ptravelz_label = QtWidgets.QLabel('%s:' % _("Probe Z travel"))
self.ptravelz_label.setToolTip(
_("The safe Z for probe travelling between probe points.")
)
self.ptravelz_entry = FCDoubleSpinner()
self.ptravelz_entry.set_precision(self.decimals)
self.ptravelz_entry.set_range(0.0000, 9999.9999)
grid0.addWidget(self.ptravelz_label, 18, 0)
grid0.addWidget(self.ptravelz_entry, 18, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
self.pdepth_label.setToolTip(
_("The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units.")
)
self.pdepth_entry = FCDoubleSpinner()
self.pdepth_entry.set_precision(self.decimals)
self.pdepth_entry.set_range(-99999.9999, 0.0000)
grid0.addWidget(self.pdepth_label, 20, 0)
grid0.addWidget(self.pdepth_entry, 20, 1)
# Probe feedrate
self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
self.feedrate_probe_label.setToolTip(
_("The feedrate used while the probe is probing.")
)
self.feedrate_probe_entry = FCDoubleSpinner()
self.feedrate_probe_entry.set_precision(self.decimals)
self.feedrate_probe_entry.set_range(0, 99999.9999)
grid0.addWidget(self.feedrate_probe_label, 22, 0)
grid0.addWidget(self.feedrate_probe_entry, 22, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 23, 0, 1, 2)
self.al_controller_label = FCLabel('<b>%s</b>:' % _("Controller"))
self.al_rows_label.setToolTip(
_("The kind of controller for which to generate\n"
"height map gcode.")
)
self.al_controller_combo = FCComboBox()
self.al_controller_combo.addItems(["MACH", "LinuxCNC"])
grid0.addWidget(self.al_controller_label, 15, 0)
grid0.addWidget(self.al_controller_combo, 15, 1)
self.al_controller_combo.addItems(["MACH3", "MACH4", "LinuxCNC", "GRBL"])
grid0.addWidget(self.al_controller_label, 24, 0)
grid0.addWidget(self.al_controller_combo, 24, 1)
self.h_gcode_button = FCButton(_("Generate Height Map GCode"))
grid0.addWidget(self.h_gcode_button, 17, 0, 1, 2)
# #############################################################################################################
# ########################## GRBL frame #######################################################################
# #############################################################################################################
self.grbl_frame = QtWidgets.QFrame()
self.grbl_frame.setContentsMargins(0, 0, 0, 0)
grid0.addWidget(self.grbl_frame, 26, 0, 1, 2)
self.import_heights_button = FCButton(_("Import Height Map"))
grid0.addWidget(self.import_heights_button, 19, 0, 1, 2)
self.grbl_box = QtWidgets.QVBoxLayout()
self.grbl_box.setContentsMargins(0, 0, 0, 0)
self.grbl_frame.setLayout(self.grbl_box)
grbl_grid = QtWidgets.QGridLayout()
grbl_grid.setColumnStretch(0, 0)
grbl_grid.setColumnStretch(1, 1)
grbl_grid.setColumnStretch(2, 0)
self.grbl_box.addLayout(grbl_grid)
# GRBL CONNECT
self.grbl_connect_label = FCLabel('<b>%s</b>:' % _("GRBL Connect"))
self.grbl_connect_label.setToolTip(
_("Setup and connect to GRBL controller.")
)
grbl_grid.addWidget(self.grbl_connect_label, 0, 0, 1, 2)
# COM list
self.com_list_label = FCLabel('%s:' % _("COM list"))
self.com_list_label.setToolTip(
_("Lists the available serial ports.")
)
self.com_list_combo = FCComboBox()
self.com_search_button = FCButton(_("Search"))
self.com_search_button.setToolTip(
_("Search for the available serial ports.")
)
grbl_grid.addWidget(self.com_list_label, 2, 0)
grbl_grid.addWidget(self.com_list_combo, 2, 1)
grbl_grid.addWidget(self.com_search_button, 2, 2)
# BAUDRATES list
self.baudrates_list_label = FCLabel('%s:' % _("Baud rates"))
self.baudrates_list_label.setToolTip(
_("Lists the available serial ports.")
)
self.baudrates_list_combo = FCComboBox()
cbmodel = QtCore.QStringListModel()
self.baudrates_list_combo.setModel(cbmodel)
self.baudrates_list_combo.addItems(
['9600', '19200', '38400', '57600', '115200', '230400', '460800', '500000', '576000', '921600', '1000000',
'1152000', '1500000', '2000000'])
self.baudrates_list_combo.setCurrentText('115200')
self.com_connect_button = FCButton(_("(Dis)Connect"))
self.com_connect_button.setToolTip(
_("Connect to the selected port with the selected baud rate.")
)
grbl_grid.addWidget(self.baudrates_list_label, 4, 0)
grbl_grid.addWidget(self.baudrates_list_combo, 4, 1)
grbl_grid.addWidget(self.com_connect_button, 4, 2)
# New baudrate
self.new_bd_label = FCLabel('%s:' % _("New"))
self.new_bd_label.setToolTip(
_("New, custom baudrate.")
)
self.new_baudrate_entry = FCSpinner()
self.new_baudrate_entry.set_range(40, 9999999)
self.add_bd_button = FCButton(_("Add"))
self.add_bd_button.setToolTip(
_("Add the specified custom baudrate to the list.")
)
grbl_grid.addWidget(self.new_bd_label, 6, 0)
grbl_grid.addWidget(self.new_baudrate_entry, 6, 1)
grbl_grid.addWidget(self.add_bd_button, 6, 2)
self.del_bd_button = FCButton(_("Delete selected baudrate"))
grbl_grid.addWidget(self.del_bd_button, 8, 0, 1, 3)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 21, 0, 1, 2)
grbl_grid.addWidget(separator_line, 10, 0, 1, 3)
# GRBL CONTROL
self.grbl_control_label = FCLabel('<b>%s</b>:' % _("GRBL Control"))
self.grbl_control_label.setToolTip(
_("Send commands to GRBL controller.")
)
grbl_grid.addWidget(self.grbl_control_label, 12, 0, 1, 3)
# CUSTOM COMMAND
self.grbl_command_label = FCLabel('%s:' % _("Command"))
self.grbl_command_label.setToolTip(
_("Send a custom command to GRBL.")
)
self.grbl_command_entry = FCEntry()
self.grbl_send_button = FCButton(_("Send"))
self.grbl_send_button.setToolTip(
_("Send a custom command to GRBL.")
)
grbl_grid.addWidget(self.grbl_command_label, 14, 0)
grbl_grid.addWidget(self.grbl_command_entry, 14, 1)
grbl_grid.addWidget(self.grbl_send_button, 14, 2)
# ZERO ALL AXES
self.grbl_zero_button = FCButton(_("ZERO all axes"))
self.grbl_zero_button.setToolTip(
_("Zero all CNC axes at current position.")
)
grbl_grid.addWidget(self.grbl_zero_button, 16, 0, 1, 3)
# GET HEIGHT MAP
self.grbl_get_heightmap_button = FCButton(_("Get Height Map"))
self.grbl_get_heightmap_button.setToolTip(
_("Will send the probing GCode to the GRBL controller\n"
"and wait for the Z probing data.")
)
grbl_grid.addWidget(self.grbl_get_heightmap_button, 18, 0, 1, 3)
self.grbl_frame.hide()
# #############################################################################################################
height_lay = QtWidgets.QHBoxLayout()
self.h_gcode_button = FCButton(_("Generate Height Map GCode"))
self.h_gcode_button.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
height_lay.addWidget(self.h_gcode_button)
self.view_h_gcode_button = QtWidgets.QToolButton()
self.view_h_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/find32.png'))
# self.view_h_gcode_button.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
self.view_h_gcode_button.setToolTip(
_("View the probing GCode.")
)
# height_lay.addStretch()
height_lay.addWidget(self.view_h_gcode_button)
grid0.addLayout(height_lay, 28, 0, 1, 2)
self.import_heights_button = FCButton(_("Import Height Map"))
grid0.addWidget(self.import_heights_button, 30, 0, 1, 2)
self.h_gcode_button.hide()
self.import_heights_button.hide()
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 31, 0, 1, 2)
self.al_button = FCButton(_("Apply Autolevel map"))
grid0.addWidget(self.al_button, 32, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 34, 0, 1, 2)
# ####################
# ## Export G-Code ##
@ -2162,14 +2367,14 @@ class CNCObjectUI(ObjectUI):
self.custom_box.addWidget(self.export_gcode_button)
self.custom_box.addStretch()
self.al_testpoints_table.setRowCount(0)
self.al_testpoints_table.resizeColumnsToContents()
self.al_testpoints_table.resizeRowsToContents()
v_header = self.al_testpoints_table.verticalHeader()
self.al_probe_points_table.setRowCount(0)
self.al_probe_points_table.resizeColumnsToContents()
self.al_probe_points_table.resizeRowsToContents()
v_header = self.al_probe_points_table.verticalHeader()
v_header.hide()
self.al_testpoints_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.al_probe_points_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
h_header = self.al_testpoints_table.horizontalHeader()
h_header = self.al_probe_points_table.horizontalHeader()
h_header.setMinimumSectionSize(10)
h_header.setDefaultSectionSize(70)
h_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
@ -2177,8 +2382,8 @@ class CNCObjectUI(ObjectUI):
h_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
h_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
self.al_testpoints_table.setMinimumHeight(self.al_testpoints_table.getHeight())
self.al_testpoints_table.setMaximumHeight(self.al_testpoints_table.getHeight())
self.al_probe_points_table.setMinimumHeight(self.al_probe_points_table.getHeight())
self.al_probe_points_table.setMaximumHeight(self.al_probe_points_table.getHeight())
# Signals
self.sal_cb.stateChanged.connect(lambda state: self.al_frame.show() if state else self.al_frame.hide())

View File

@ -30,6 +30,8 @@ except Exception:
import os
import sys
import serial
import glob
import math
import gettext
@ -130,6 +132,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
# determine if the GCode was generated out of a Excellon object or a Geometry object
self.origin_kind = None
self.coords_decimals = 4
self.fr_decimals = 2
# used for parsing the GCode lines to adjust the GCode when the GCode is offseted or scaled
gcodex_re_string = r'(?=.*(X[-\+]?\d*\.\d*))'
self.g_x_re = re.compile(gcodex_re_string)
@ -152,9 +157,11 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.annotation = self.app.plotcanvas.new_text_group(collection=self.text_col)
self.gcode_editor_tab = None
self.gcode_viewer_tab = None
self.source_file = ''
self.units_found = self.app.defaults['units']
self.probing_gcode_text = ''
# store the current selection shape status to be restored after manual adding test points
self.old_selection_state = self.app.defaults['global_selection_shape']
@ -188,6 +195,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
}
'''
self.al_geometry_dict = {}
self.grbl_ser_port = None
# Attributes to be included in serialization
# Always append to it because it carries contents
@ -403,7 +411,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
tool_idx = 0
n = len(self.al_geometry_dict)
self.ui.al_testpoints_table.setRowCount(n)
self.ui.al_probe_points_table.setRowCount(n)
for id_key, value in self.al_geometry_dict.items():
tool_idx += 1
@ -421,14 +429,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
coords_item.setFlags(QtCore.Qt.ItemIsEnabled)
height_item.setFlags(QtCore.Qt.ItemIsEnabled)
self.ui.al_testpoints_table.setItem(row_no, 0, t_id) # Tool name/id
self.ui.al_testpoints_table.setItem(row_no, 1, coords_item) # X-Y coords
self.ui.al_testpoints_table.setItem(row_no, 2, height_item) # Determined Height
self.ui.al_probe_points_table.setItem(row_no, 0, t_id) # Tool name/id
self.ui.al_probe_points_table.setItem(row_no, 1, coords_item) # X-Y coords
self.ui.al_probe_points_table.setItem(row_no, 2, height_item) # Determined Height
self.ui.al_testpoints_table.resizeColumnsToContents()
self.ui.al_testpoints_table.resizeRowsToContents()
self.ui.al_probe_points_table.resizeColumnsToContents()
self.ui.al_probe_points_table.resizeRowsToContents()
h_header = self.ui.al_testpoints_table.horizontalHeader()
h_header = self.ui.al_probe_points_table.horizontalHeader()
h_header.setMinimumSectionSize(10)
h_header.setDefaultSectionSize(70)
h_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
@ -436,13 +444,19 @@ class CNCJobObject(FlatCAMObj, CNCjob):
h_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
h_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
self.ui.al_testpoints_table.setMinimumHeight(self.ui.al_testpoints_table.getHeight())
self.ui.al_testpoints_table.setMaximumHeight(self.ui.al_testpoints_table.getHeight())
self.ui.al_probe_points_table.setMinimumHeight(self.ui.al_probe_points_table.getHeight())
self.ui.al_probe_points_table.setMaximumHeight(self.ui.al_probe_points_table.getHeight())
if self.ui.al_testpoints_table.model().rowCount():
if self.ui.al_probe_points_table.model().rowCount():
self.ui.voronoi_cb.setDisabled(False)
self.ui.grbl_get_heightmap_button.setDisabled(False)
self.ui.h_gcode_button.setDisabled(False)
self.ui.view_h_gcode_button.setDisabled(False)
else:
self.ui.voronoi_cb.setDisabled(True)
self.ui.grbl_get_heightmap_button.setDisabled(True)
self.ui.h_gcode_button.setDisabled(True)
self.ui.view_h_gcode_button.setDisabled(True)
def set_ui(self, ui):
FlatCAMObj.set_ui(self, ui)
@ -527,18 +541,27 @@ class CNCJobObject(FlatCAMObj, CNCjob):
'<span style="color:green;"><b>Basic</b></span>'
))
# self.ui.cnc_frame.hide()
self.ui.sal_cb.hide()
else:
self.ui.level.setText(_(
'<span style="color:red;"><b>Advanced</b></span>'
))
# self.ui.cnc_frame.show()
self.ui.sal_cb.show()
self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click)
self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click)
self.ui.review_gcode_button.clicked.connect(self.on_edit_code_click)
self.ui.editor_button.clicked.connect(lambda: self.app.object2editor())
# autolevelling signals
self.ui.al_mode_radio.activated_custom.connect(self.on_mode_radio)
self.ui.al_controller_combo.currentIndexChanged.connect(self.on_controller_change)
self.ui.com_search_button.clicked.connect(self.on_search_ports)
self.ui.add_bd_button.clicked.connect(self.on_add_baudrate_grbl)
self.ui.del_bd_button.clicked.connect(self.on_delete_baudrate_grbl)
self.ui.com_connect_button.clicked.connect(self.on_connect_grbl)
self.ui.view_h_gcode_button.clicked.connect(self.on_view_probing_gcode)
self.ui.h_gcode_button.clicked.connect(self.on_generate_probing_gcode)
# self.ui.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
@ -550,6 +573,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.source_file = gc.getvalue()
self.ui.al_mode_radio.set_value('grid')
self.on_controller_change()
# def on_cnc_custom_parameters(self, signal_text):
# if signal_text == 'Parameters':
@ -564,7 +588,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.ui.exc_cnc_tools_table.cellWidget(row, 6).clicked.connect(self.on_plot_cb_click_table)
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
self.ui.al_add_button.clicked.connect(self.on_add_al_testpoints)
self.ui.al_add_button.clicked.connect(self.on_add_al_probepoints)
self.ui.show_al_table.stateChanged.connect(self.on_show_al_table)
def ui_disconnect(self):
@ -595,14 +619,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
except (TypeError, AttributeError):
pass
def on_add_al_testpoints(self):
def on_add_al_probepoints(self):
# create the solid_geo
solid_geo = [geo['geom'] for geo in self.gcode_parsed if geo['kind'][0] == 'C']
solid_geo = unary_union(solid_geo)
# reset al table
self.ui.al_testpoints_table.setRowCount(0)
self.ui.al_probe_points_table.setRowCount(0)
# reset the al dict
self.al_geometry_dict.clear()
@ -795,11 +819,11 @@ class CNCJobObject(FlatCAMObj, CNCjob):
l_x, l_y = self.app.on_jump_to()
def on_show_al_table(self, state):
self.ui.al_testpoints_table.show() if state else self.ui.al_testpoints_table.hide()
self.ui.al_probe_points_table.show() if state else self.ui.al_probe_points_table.hide()
def on_mode_radio(self, val):
# reset al table
self.ui.al_testpoints_table.setRowCount(0)
self.ui.al_probe_points_table.setRowCount(0)
# reset the al dict
self.al_geometry_dict.clear()
@ -818,6 +842,269 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.ui.al_columns_entry.setDisabled(False)
self.ui.al_columns_label.setDisabled(False)
def on_controller_change(self):
if self.ui.al_controller_combo.get_value() == 'GRBL':
self.ui.h_gcode_button.hide()
self.ui.view_h_gcode_button.hide()
self.ui.import_heights_button.hide()
self.ui.grbl_frame.show()
self.on_search_ports(muted=True)
else:
self.ui.h_gcode_button.show()
self.ui.view_h_gcode_button.show()
self.ui.import_heights_button.show()
self.ui.grbl_frame.hide()
def list_serial_ports(self):
"""
Lists serial port names.
From here: https://stackoverflow.com/questions/12090503/listing-available-com-ports-with-python
:raises EnvironmentError: On unsupported or unknown platforms
:returns: A list of the serial ports available on the system
"""
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
s = serial.Serial()
for port in ports:
s.port = port
try:
s.open()
s.close()
result.append(port)
except (OSError, serial.SerialException):
# result.append(port + " (in use)")
pass
return result
def on_search_ports(self, muted=None):
port_list = self.list_serial_ports()
self.ui.com_list_combo.clear()
self.ui.com_list_combo.addItems(port_list)
if muted is not True:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("COM list updated ..."))
def on_connect_grbl(self):
port_name = self.ui.com_list_combo.currentText()
if " (" in port_name:
port_name = port_name.rpartition(" (")[0]
baudrate = int(self.ui.baudrates_list_combo.currentText())
try:
self.grbl_ser_port = serial.serial_for_url(port_name, baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=0.1,
xonxoff=False,
rtscts=False)
self.app.inform.emit("%s: %s" % (_("Port connected"), port_name))
self.ui.com_connect_button.setStyleSheet("QPushButton {color: seagreen;}")
# Toggle DTR to reset the controller loaded with GRBL (Arduino, ESP32, etc)
try:
self.grbl_ser_port.dtr = False
except IOError:
pass
self.grbl_ser_port.reset_input_buffer()
try:
self.grbl_ser_port.dtr = True
except IOError:
pass
except serial.SerialException:
self.grbl_ser_port = serial.Serial()
self.grbl_ser_port.port = port_name
self.grbl_ser_port.close()
self.ui.com_connect_button.setStyleSheet("")
self.app.inform.emit("%s: %s" % (_("Port is connected. Disconnecting"), port_name))
except Exception:
self.app.inform.emit("[ERROR_NOTCL] %s: %s" % (_("Could not connect to port"), port_name))
def on_add_baudrate_grbl(self):
new_bd = str(self.ui.new_baudrate_entry.get_value())
if int(new_bd) >= 40 and new_bd not in self.ui.baudrates_list_combo.model().stringList():
self.ui.baudrates_list_combo.addItem(new_bd)
self.ui.baudrates_list_combo.setCurrentText(new_bd)
def on_delete_baudrate_grbl(self):
current_idx = self.ui.baudrates_list_combo.currentIndex()
self.ui.baudrates_list_combo.removeItem(current_idx)
def probing_gcode(self, coords, pr_travel, probe_fr, pr_depth, controller):
"""
:param coords: a list of (x, y) tuples of probe points coordinates
:type coords: list
:param pr_travel: the height (z) where the probe travel between probe points
:type pr_travel: float
:param probe_fr: feedrate when probing
:type probe_fr: float
:param pr_depth: how much to lower the probe searching for contact
:type pr_depth: float
:param controller: a string with the name of the GCode sender for which to create the probing GCode.
Can be: 'MACH3', 'MACH4', 'LinuxCNC', 'GRBL'
:type controller: str
:return: Probing GCode
:rtype: str
"""
p_gcode = ''
header = ''
# commands
if controller == 'MACH3':
probing_command = 'G31'
probing_var = '#2002'
openfile_command = 'M40'
closefile_command = 'M41'
elif controller == 'MACH4':
probing_command = 'G31'
probing_var = '#5063'
openfile_command = 'M40'
closefile_command = 'M41'
elif controller == 'LinuxCNC':
probing_command = 'G38.2'
probing_var = '#5422'
openfile_command = '(PROBEOPEN a_probing_points_file.txt)'
closefile_command = '(PROBECLOSE)'
else:
log.debug("CNCJobObject.probing_gcode() -> controller not supported")
return
# #############################################################################################################
# ########################### GCODE construction ##############################################################
# #############################################################################################################
# header
p_gcode += header + '\n\n'
# supplementary message for LinuxCNC
if controller == 'LinuxCNC':
probing_var += "The file with the stored probing points can be found\n" \
"in the configuration folder for LinuxCNC.\n" \
"The name of the file is: a_probing_points_file.txt.\n"
# units
p_gcode += 'G21\n' if self.units == 'MM' else 'G20\n'
# reference mode = absolute
p_gcode += 'G90\n'
# open a new file
p_gcode += openfile_command + '\n'
# move to safe height (probe travel Z)
p_gcode += 'G0 Z%s\n' % str(self.app.dec_format(pr_travel, self.coords_decimals))
# probing points
for idx, xy_tuple in enumerate(coords, 1): # index starts from 1
x = xy_tuple[0]
y = xy_tuple[1]
# move to probing point
p_gcode += "G0 X%sY%s\n" % (
str(self.app.dec_format(x, self.coords_decimals)),
str(self.app.dec_format(y, self.coords_decimals))
)
# do the probing
p_gcode += "%s Z%s F%s\n" % (
probing_command,
str(self.app.dec_format(pr_depth, self.coords_decimals)),
str(self.app.dec_format(probe_fr, self.fr_decimals)),
)
# store in a global numeric variable the value of the detected probe Z
# I offset the global numeric variable by 500 so it does not conflict with something else
temp_var = int(idx + 500)
p_gcode += "#%d = %s\n" % (temp_var, probing_var)
# move to safe height (probe travel Z)
p_gcode += 'G0 Z%s\n' % str(self.app.dec_format(pr_travel, self.coords_decimals))
# close the file
p_gcode += closefile_command + '\n'
# finish the GCode
p_gcode += 'M2'
return p_gcode
def on_generate_probing_gcode(self):
coords = []
for id_key, value in self.al_geometry_dict.items():
x = value['point'].x
y = value['point'].y
coords.append(
(
self.app.dec_format(x, dec=self.app.decimals),
self.app.dec_format(y, dec=self.app.decimals)
)
)
pr_travel = self.ui.ptravelz_entry.get_value()
probe_fr = self.ui.feedrate_probe_entry.get_value()
pr_depth = self.ui.pdepth_entry.get_value()
controller = self.ui.al_controller_combo.get_value()
self.probing_gcode_text = self.probing_gcode(coords, pr_travel, probe_fr, pr_depth, controller)
def on_view_probing_gcode(self):
self.app.proc_container.view.set_busy(_("Loading..."))
gco = self.probing_gcode_text
if gco is None or gco == '':
self.app.inform.emit('[WARNING_NOTCL] %s...' % _('There is nothing to view'))
return
self.gcode_viewer_tab = AppTextEditor(app=self.app, plain_text=True)
# add the tab if it was closed
self.app.ui.plot_tab_area.addTab(self.gcode_viewer_tab, '%s' % _("Code Viewer"))
self.gcode_viewer_tab.setObjectName('code_viewer_tab')
# delete the absolute and relative position and messages in the infobar
self.app.ui.position_label.setText("")
self.app.ui.rel_position_label.setText("")
self.gcode_viewer_tab.code_editor.completer_enable = False
self.gcode_viewer_tab.buttonRun.hide()
# Switch plot_area to CNCJob tab
self.app.ui.plot_tab_area.setCurrentWidget(self.gcode_viewer_tab)
self.gcode_viewer_tab.t_frame.hide()
# then append the text from GCode to the text editor
try:
self.gcode_viewer_tab.load_text(gco, move_to_start=True, clear_text=True)
except Exception as e:
log.debug('FlatCAMCNCJob.on_edit_code_click() -->%s' % str(e))
return
self.gcode_viewer_tab.t_frame.show()
self.app.proc_container.view.set_idle()
self.gcode_viewer_tab.buttonSave.hide()
self.gcode_viewer_tab.buttonOpen.hide()
self.gcode_viewer_tab.buttonPrint.hide()
self.gcode_viewer_tab.buttonPreview.hide()
self.gcode_viewer_tab.buttonReplace.hide()
self.gcode_viewer_tab.sel_all_cb.hide()
self.gcode_viewer_tab.entryReplace.hide()
self.gcode_viewer_tab.code_editor.setReadOnly(True)
self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Viewer'))
def on_updateplot_button_click(self, *args):
"""
Callback for the "Updata Plot" button. Reads the form for updates
@ -904,7 +1191,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
def on_edit_code_click(self, *args):
"""
Handler activated by a button clicked when editing GCode.
Handler activated by a button clicked when reviewing GCode.
:param args:
:return: