- stored solid_geometry of Excellon object in the self.tools dictionary

- finished the solid_geometry restore after edit in Excellon Editor
- finished plotting selection for each tool in the Excellon Tool Table
- fixed the camlib.Excellon.bounds() function for the new type of Excellon geometry therefore fixed the canvas selection, too
This commit is contained in:
Marius Stanciu 2019-02-13 01:22:09 +02:00 committed by Marius S
parent 02793f7ae2
commit c94679919d
5 changed files with 257 additions and 81 deletions

View File

@ -4306,6 +4306,9 @@ class FlatCAMExcEditor(QtCore.QObject):
spec = {"C": float(tool_dia[0])}
self.new_tools[name] = spec
# add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
self.new_tools[name]['solid_geometry'] = []
# create the self.drills for the new Excellon object (the one with edited content)
for point in tool_dia[1]:
self.new_drills.append(
@ -4314,6 +4317,9 @@ class FlatCAMExcEditor(QtCore.QObject):
'tool': str(current_tool)
}
)
# repopulate the 'solid_geometry' for each tool
poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4))
self.new_tools[name]['solid_geometry'].append(poly)
if self.is_modified is True:
if "_edit" in self.edited_obj_name:

View File

@ -1134,10 +1134,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
t_offset = self.app.defaults['excellon_offset']
tool_offset_item = QtWidgets.QTableWidgetItem('%s' % str(t_offset))
plot_item = FCCheckBox()
plot_item.setLayoutDirection(QtCore.Qt.RightToLeft)
if self.ui.plot_cb.isChecked():
plot_item.setChecked(True)
self.ui.tools_table.setItem(self.tool_row, 1, dia) # Diameter
self.ui.tools_table.setItem(self.tool_row, 2, drill_count) # Number of drills per tool
self.ui.tools_table.setItem(self.tool_row, 3, slot_count) # Number of drills per tool
self.ui.tools_table.setItem(self.tool_row, 4, tool_offset_item) # Tool offset
self.ui.tools_table.setCellWidget(self.tool_row, 5, plot_item)
self.tool_row += 1
@ -1201,12 +1207,28 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
self.ui.tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
horizontal_header = self.ui.tools_table.horizontalHeader()
horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
horizontal_header.setMinimumSectionSize(10)
horizontal_header.setDefaultSectionSize(70)
horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
horizontal_header.resizeSection(0, 20)
horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents)
horizontal_header.setSectionResizeMode(5, QtWidgets.QHeaderView.Fixed)
horizontal_header.resizeSection(5, 17)
self.ui.tools_table.setColumnWidth(5, 17)
# horizontal_header.setStretchLastSection(True)
# horizontal_header.setColumnWidth(2, QtWidgets.QHeaderView.ResizeToContents)
# horizontal_header.setStretchLastSection(True)
self.ui.tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.ui.tools_table.setSortingEnabled(False)
self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight())
@ -1233,6 +1255,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
# we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
self.ui.tools_table.itemChanged.connect(self.on_tool_offset_edit)
self.ui_connect()
def set_ui(self, ui):
"""
Configures the user interface for this object.
@ -1297,6 +1321,24 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
self.ui.pp_excellon_name_cb.activated.connect(self.on_pp_changed)
def ui_connect(self):
for row in range(self.ui.tools_table.rowCount() - 2):
self.ui.tools_table.cellWidget(row, 5).clicked.connect(self.on_plot_cb_click_table)
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
def ui_disconnect(self):
for row in range(self.ui.tools_table.rowCount()):
try:
self.ui.tools_table.cellWidget(row, 5).clicked.disconnect()
except:
pass
try:
self.ui.plot_cb.stateChanged.disconnect()
except:
pass
def on_tool_offset_edit(self):
# if connected, disconnect the signal from the slot on item_changed as it creates issues
self.ui.tools_table.itemChanged.disconnect()
@ -1351,8 +1393,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
"""
table_tools_items = []
for x in self.ui.tools_table.selectedItems():
# from the columnCount we subtract a value of 1 which represent the last column (plot column)
# which does not have text
table_tools_items.append([self.ui.tools_table.item(x.row(), column).text()
for column in range(0, self.ui.tools_table.columnCount())])
for column in range(0, self.ui.tools_table.columnCount() - 1)])
for item in table_tools_items:
item[0] = str(item[0])
return table_tools_items
@ -1839,17 +1883,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
# self.app.worker.add_task(job_thread, [self.app])
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def on_plot_cb_click(self, *args):
if self.muted_ui:
return
self.read_form_item('plot')
def on_solid_cb_click(self, *args):
if self.muted_ui:
return
self.read_form_item('solid')
self.plot()
def convert_units(self, units):
factor = Excellon.convert_units(self, units)
@ -1875,6 +1908,89 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
self.options['startz'] = float(self.options['startz']) * factor
self.options['endz'] = float(self.options['endz']) * factor
def on_solid_cb_click(self, *args):
if self.muted_ui:
return
self.read_form_item('solid')
self.plot()
def on_plot_cb_click(self, *args):
if self.muted_ui:
return
self.plot()
self.read_form_item('plot')
self.ui_disconnect()
cb_flag = self.ui.plot_cb.isChecked()
for row in range(self.ui.tools_table.rowCount() - 2):
table_cb = self.ui.tools_table.cellWidget(row, 5)
if cb_flag:
table_cb.setChecked(True)
else:
table_cb.setChecked(False)
self.ui_connect()
def on_plot_cb_click_table(self):
# self.ui.cnc_tools_table.cellWidget(row, 2).widget().setCheckState(QtCore.Qt.Unchecked)
self.ui_disconnect()
# cw = self.sender()
# cw_index = self.ui.tools_table.indexAt(cw.pos())
# cw_row = cw_index.row()
check_row = 0
self.shapes.clear(update=True)
for tool_key in self.tools:
solid_geometry = self.tools[tool_key]['solid_geometry']
# find the geo_tool_table row associated with the tool_key
for row in range(self.ui.tools_table.rowCount()):
tool_item = int(self.ui.tools_table.item(row, 0).text())
if tool_item == int(tool_key):
check_row = row
break
if self.ui.tools_table.cellWidget(check_row, 5).isChecked():
self.options['plot'] = True
# self.plot_element(element=solid_geometry, visible=True)
# Plot excellon (All polygons?)
if self.options["solid"]:
for geo in solid_geometry:
self.add_shape(shape=geo, color='#750000BF', face_color='#C40000BF',
visible=self.options['plot'],
layer=2)
else:
for geo in solid_geometry:
self.add_shape(shape=geo.exterior, color='red', visible=self.options['plot'])
for ints in geo.interiors:
self.add_shape(shape=ints, color='green', visible=self.options['plot'])
self.shapes.redraw()
# make sure that the general plot is disabled if one of the row plot's are disabled and
# if all the row plot's are enabled also enable the general plot checkbox
cb_cnt = 0
total_row = self.ui.tools_table.rowCount()
for row in range(total_row - 2):
if self.ui.tools_table.cellWidget(row, 5).isChecked():
cb_cnt += 1
else:
cb_cnt -= 1
if cb_cnt < total_row - 2:
self.ui.plot_cb.setChecked(False)
else:
self.ui.plot_cb.setChecked(True)
self.ui_connect()
# def plot_element(self, element, color='red', visible=None, layer=None):
#
# visible = visible if visible else self.options['plot']
#
# try:
# for sub_el in element:
# self.plot_element(sub_el)
#
# except TypeError: # Element is not iterable...
# self.add_shape(shape=element, color=color, visible=visible, layer=0)
def plot(self):
# Does all the required setup and returns False
@ -3971,62 +4087,65 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
param_list = ['cutz', 'depthperpass', 'travelz', 'feedrate', 'feedrate_z', 'feedrate_rapid',
'endz', 'toolchangez']
temp_tools_dict = {}
tool_dia_copy = {}
data_copy = {}
for tooluid_key, tooluid_value in self.tools.items():
for dia_key, dia_value in tooluid_value.items():
if dia_key == 'tooldia':
dia_value *= factor
dia_value = float('%.4f' % dia_value)
tool_dia_copy[dia_key] = dia_value
if dia_key == 'offset':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'offset_value':
dia_value *= factor
tool_dia_copy[dia_key] = dia_value
if isinstance(self, FlatCAMGeometry):
temp_tools_dict = {}
tool_dia_copy = {}
data_copy = {}
for tooluid_key, tooluid_value in self.tools.items():
for dia_key, dia_value in tooluid_value.items():
if dia_key == 'tooldia':
dia_value *= factor
dia_value = float('%.4f' % dia_value)
tool_dia_copy[dia_key] = dia_value
if dia_key == 'offset':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'offset_value':
dia_value *= factor
tool_dia_copy[dia_key] = dia_value
# convert the value in the Custom Tool Offset entry in UI
try:
custom_offset = float(self.ui.tool_offset_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
# convert the value in the Custom Tool Offset entry in UI
custom_offset = None
try:
custom_offset = float(self.ui.tool_offset_entry.get_value().replace(',', '.')
)
custom_offset = float(self.ui.tool_offset_entry.get_value())
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
# try to convert comma to decimal point. if it's still not working error message and return
try:
custom_offset = float(self.ui.tool_offset_entry.get_value().replace(',', '.')
)
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
except TypeError:
pass
if custom_offset:
custom_offset *= factor
self.ui.tool_offset_entry.set_value(custom_offset)
if custom_offset:
custom_offset *= factor
self.ui.tool_offset_entry.set_value(custom_offset)
if dia_key == 'type':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'tool_type':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'data':
for data_key, data_value in dia_value.items():
# convert the form fields that are convertible
for param in param_list:
if data_key == param and data_value is not None:
data_copy[data_key] = data_value * factor
# copy the other dict entries that are not convertible
if data_key not in param_list:
data_copy[data_key] = data_value
tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
data_copy.clear()
if dia_key == 'type':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'tool_type':
tool_dia_copy[dia_key] = dia_value
if dia_key == 'data':
for data_key, data_value in dia_value.items():
# convert the form fields that are convertible
for param in param_list:
if data_key == param and data_value is not None:
data_copy[data_key] = data_value * factor
# copy the other dict entries that are not convertible
if data_key not in param_list:
data_copy[data_key] = data_value
tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
data_copy.clear()
temp_tools_dict.update({
tooluid_key: copy.deepcopy(tool_dia_copy)
})
tool_dia_copy.clear()
temp_tools_dict.update({
tooluid_key: copy.deepcopy(tool_dia_copy)
})
tool_dia_copy.clear()
self.tools.clear()
self.tools = copy.deepcopy(temp_tools_dict)
self.tools.clear()
self.tools = copy.deepcopy(temp_tools_dict)
# if there is a value in the new tool field then convert that one too
tooldia = self.ui.addtool_entry.get_value()
@ -4188,6 +4307,27 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
'''
self.cnc_tools = {}
'''
This is a dict of dictionaries. Each dict is associated with a tool present in the file. The key is the
diameter of the tools and the value is another dict that will hold the data under the following form:
{tooldia: {
'tool': int,
'nr_drills': int,
'nr_slots': int,
'offset': float,
'data': {} # a dict to hold the parameters
'gcode': "" # a string with the actual GCODE
'gcode_parsed': {} # dictionary holding the CNCJob geometry and type of geometry (cut or move)
'solid_geometry': []
},
...
}
It is populated in the FlatCAMExcellon.on_create_cncjob_click() but actually
it's done in camlib.Excellon.generate_from_excellon_by_tool()
BEWARE: I rely on the ordered nature of the Python 3.7 dictionary. Things might change ...
'''
self.exc_cnc_tools = {}
# for now it show if the plot will be done for multi-tool CNCJob (True) or for single tool
# (like the one in the TCL Command), False
self.multitool = False
@ -4223,10 +4363,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
# if the FlatCAM object is Excellon don't build the CNC Tools Table but hide it
if self.cnc_tools:
self.ui.cnc_tools_table.show()
self.ui.plot_options_label.show()
else:
self.ui.cnc_tools_table.hide()
self.ui.plot_options_label.hide()
offset = 0
tool_idx = 0

View File

@ -388,16 +388,12 @@ class ExcellonObjectUI(ObjectUI):
grid0 = QtWidgets.QGridLayout()
self.custom_box.addLayout(grid0)
self.plot_cb = FCCheckBox(label='Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
self.solid_cb = FCCheckBox(label='Solid')
self.solid_cb.setToolTip(
"Solid circles."
)
grid0.addWidget(self.solid_cb, 0, 1)
grid0.addWidget(self.solid_cb, 0, 0)
# add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
# this way I can hide/show the frame
@ -408,19 +404,31 @@ class ExcellonObjectUI(ObjectUI):
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.drills_frame.setLayout(self.tools_box)
hlay_plot = QtWidgets.QHBoxLayout()
self.tools_box.addLayout(hlay_plot)
#### Tools Drills ####
self.tools_table_label = QtWidgets.QLabel('<b>Tools Table</b>')
self.tools_table_label.setToolTip(
"Tools in this Excellon object\n"
"when are used for drilling."
)
self.tools_box.addWidget(self.tools_table_label)
hlay_plot.addWidget(self.tools_table_label)
# Plot CB
self.plot_cb = FCCheckBox('Plot Object')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
hlay_plot.addStretch()
hlay_plot.addWidget(self.plot_cb)
self.tools_table = FCTable()
self.tools_box.addWidget(self.tools_table)
self.tools_table.setColumnCount(5)
self.tools_table.setHorizontalHeaderLabels(['#', 'Diameter', 'D', 'S', 'Offset'])
self.tools_table.setColumnCount(6)
self.tools_table.setHorizontalHeaderLabels(['#', 'Diameter', 'D', 'S', 'Offset', 'P'])
self.tools_table.setSortingEnabled(False)
self.tools_table.horizontalHeaderItem(0).setToolTip(
@ -440,6 +448,8 @@ class ExcellonObjectUI(ObjectUI):
"Some drill bits (the larger ones) need to drill deeper\n"
"to create the desired exit hole diameter due of the tip shape.\n"
"The value here can compensate the Cut Z parameter.")
self.tools_table.horizontalHeaderItem(5).setToolTip(
"Toggle display of the drills for the current tool.")
self.empty_label = QtWidgets.QLabel('')
self.tools_box.addWidget(self.empty_label)

View File

@ -12,11 +12,16 @@ CAD program, and create G-Code for Isolation routing.
12.02.2019
- whenever a FlatCAM tool is activated, if the notebook side is hidden it will be unhidden
- reactivated the Voronoi classed
- reactivated the Voronoi classes
- added a new parameter named Offset in the Excellon tool table - work in progress
- finished work on Offset parameter in Excellon Object (Excellon Editor, camlib, FlatCAMObj updated to take this param in consideration)
- fixed a bug where in Excellon editor when editing a file, a tool was automatically added. That is supposed to happen only for empty newly created Excellon Objects.
- starting to work on storing the solid_geometry for each tool in part in Excellon Object
- stored solid_geometry of Excellon object in the self.tools dictionary
- finished the solid_geometry restore after edit in Excellon Editor
- finished plotting selection for each tool in the Excellon Tool Table
- fixed the camlib.Excellon.bounds() function for the new type of Excellon geometry therefore fixed the canvas selection, too
10.02.2019

View File

@ -4073,7 +4073,12 @@ class Excellon(Geometry):
:return: None
"""
self.solid_geometry = []
try:
# clear the solid_geometry in self.tools
for tool in self.tools:
self.tools[tool]['solid_geometry'][:] = []
for drill in self.drills:
# poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
if drill['tool'] is '':
@ -4096,7 +4101,7 @@ class Excellon(Geometry):
lines_string = LineString([start, stop])
poly = lines_string.buffer(slot_tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
# self.solid_geometry.append(poly)
self.tools[drill['tool']]['solid_geometry'].append(poly)
self.tools[slot['tool']]['solid_geometry'].append(poly)
except Exception as e:
log.debug("Excellon geometry creation failed due of ERROR: %s" % str(e))
@ -4139,9 +4144,9 @@ class Excellon(Geometry):
# now it can get bounds for nested lists of objects
log.debug("Excellon() -> bounds()")
if self.solid_geometry is None:
log.debug("solid_geometry is None")
return 0, 0, 0, 0
# if self.solid_geometry is None:
# log.debug("solid_geometry is None")
# return 0, 0, 0, 0
def bounds_rec(obj):
if type(obj) is list:
@ -4169,8 +4174,19 @@ class Excellon(Geometry):
# it's a Shapely object, return it's bounds
return obj.bounds
bounds_coords = bounds_rec(self.solid_geometry)
return bounds_coords
minx_list = []
miny_list = []
maxx_list = []
maxy_list = []
for tool in self.tools:
minx, miny, maxx, maxy = bounds_rec(self.tools[tool]['solid_geometry'])
minx_list.append(minx)
miny_list.append(miny)
maxx_list.append(maxx)
maxy_list.append(maxy)
return (min(minx_list), min(miny_list), max(maxx_list), max(maxy_list))
def convert_units(self, units):
"""
@ -5535,7 +5551,7 @@ class CNCjob(Geometry):
return "fail"
gobj = self.codes_split(line)
print(gobj)
## Units
if 'G' in gobj and (gobj['G'] == 20.0 or gobj['G'] == 21.0):
self.units = {20.0: "IN", 21.0: "MM"}[gobj['G']]