- 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:
parent
cc7c525be1
commit
fd895e3c21
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue