- fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None

- fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
- made the shell toggle shortcut key work when focused on Selected Tab; toggle units shortcut also
- changed the messages for Units COnversion
This commit is contained in:
Marius Stanciu 2019-02-06 14:03:59 +02:00 committed by Marius
parent b589292c0f
commit dedf8c09de
5 changed files with 236 additions and 177 deletions

View File

@ -1659,7 +1659,10 @@ class App(QtCore.QObject):
return self.defaults["global_last_folder"]
def get_last_save_folder(self):
return self.defaults["global_last_save_folder"]
loc = self.defaults["global_last_save_folder"]
if loc is None:
loc = self.defaults["global_last_folder"]
return loc
def report_usage(self, resource):
"""
@ -2070,6 +2073,8 @@ class App(QtCore.QObject):
except:
self.inform.emit("[ERROR_NOTCL] Failed to write defaults to file.")
return
self.file_saved.emit("preferences", filename)
self.inform.emit("[success]Exported Defaults to %s" % filename)
def on_preferences_open_folder(self):
@ -2825,6 +2830,9 @@ class App(QtCore.QObject):
current.to_form()
self.plot_all()
self.inform.emit("[success]Converted units to %s" % self.options["units"])
# self.ui.units_label.setText("[" + self.options["units"] + "]")
self.set_screen_units(self.options["units"])
else:
# Undo toggling
self.toggle_units_ignore = True
@ -2833,11 +2841,9 @@ class App(QtCore.QObject):
else:
self.general_options_form.general_app_group.units_radio.set_value('MM')
self.toggle_units_ignore = False
self.inform.emit("[WARNING_NOTCL]Units conversion cancelled.")
self.options_read_form()
self.inform.emit("Converted units to %s" % self.options["units"])
#self.ui.units_label.setText("[" + self.options["units"] + "]")
self.set_screen_units(self.options["units"])
def on_toggle_units_click(self):
if self.options["units"] == 'MM':
@ -6044,7 +6050,7 @@ class App(QtCore.QObject):
self.defaults["global_last_folder"] = os.path.split(str(filename))[0]
def register_save_folder(self, filename):
self.defaults['global_last_save_folder'] = os.path.split(str(filename))[0]
self.defaults["global_last_save_folder"] = os.path.split(str(filename))[0]
def set_progress_bar(self, percentage, text=""):
self.ui.progress_bar.setValue(int(percentage))

View File

@ -1508,6 +1508,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
if event.key() == QtCore.Qt.Key_3:
self.app.on_select_tab('tool')
if event.key == QtCore.Qt.Key_Q:
self.app.on_toggle_units_click()
if event.key() == QtCore.Qt.Key_S:
self.app.on_toggle_shell()
# Show shortcut list
if event.key() == QtCore.Qt.Key_Ampersand:
self.app.on_shortcut_list()

View File

@ -1564,7 +1564,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
return False, "Error: No tools."
for tool in tools:
if tooldia > self.tools[tool]["C"]:
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
adj_toolstable_tooldia = float('%.4f' % float(tooldia))
adj_file_tooldia = float('%.4f' % float(self.tools[tool]["C"]))
if adj_toolstable_tooldia > adj_file_tooldia + 0.0001:
self.app.inform.emit("[ERROR_NOTCL] Milling tool for SLOTS is larger than hole size. Cancelled.")
return False, "Error: Milling tool is larger than hole."
@ -1590,7 +1593,12 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
# we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
for slot in self.slots:
if slot['tool'] in tools:
buffer_value = (float(self.tools[slot['tool']]["C"]) / 2) - float(tooldia / 2)
toolstable_tool = float('%.4f' % float(tooldia))
file_tool = float('%.4f' % float(self.tools[tool]["C"]))
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
# for the file_tool (tooldia actually)
buffer_value = float(file_tool / 2) - float(toolstable_tool / 2) + 0.0001
if buffer_value == 0:
start = slot['start']
stop = slot['stop']
@ -1729,14 +1737,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
# job_obj.options["tooldia"] =
tools_csv = ','.join(tools)
job_obj.generate_from_excellon_by_tool(self, tools_csv,
drillz=self.options['drillz'],
toolchange=self.options["toolchange"],
toolchangez=self.options["toolchangez"],
startz=self.options["startz"],
endz=self.options["endz"],
excellon_optimization_type=self.options["optimization_type"])
ret_val = job_obj.generate_from_excellon_by_tool(self, tools_csv,
drillz=self.options['drillz'],
toolchange=self.options["toolchange"],
toolchangez=self.options["toolchangez"],
startz=self.options["startz"],
endz=self.options["endz"],
excellon_optimization_type=self.app.defaults[
"excellon_optimization_type"])
if ret_val == 'fail':
return 'fail'
app_obj.progress.emit(50)
job_obj.gcode_parse()
@ -3125,10 +3135,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
xmin = self.options['xmin']
ymin = self.options['ymin']
xmax = self.options['xmax']
ymax = self.options['ymax']
try:
xmin = self.options['xmin']
ymin = self.options['ymin']
xmax = self.options['xmax']
ymax = self.options['ymax']
except Exception as e:
log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e))
msg = "[ERROR] An internal error has ocurred. See shell.\n"
msg += 'FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s' % str(e)
msg += traceback.format_exc()
self.app.inform.emit(msg)
return
# Object initialization function for app.new_object()
# RUNNING ON SEPARATE THREAD!
@ -4267,14 +4285,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
_filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
"G-Code Files (*.g-code);;All Files (*.*)"
dir_file_to_save = self.app.get_last_save_folder() + '/' + str(name)
try:
filename = str(QtWidgets.QFileDialog.getSaveFileName(
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
caption="Export Machine Code ...",
directory=self.app.get_last_save_folder() + '/' + name,
directory=dir_file_to_save,
filter=_filter_
)[0])
)
except TypeError:
filename = str(QtWidgets.QFileDialog.getSaveFileName(caption="Export Machine Code ...", filter=_filter_)[0])
filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export Machine Code ...", filter=_filter_)
filename = str(filename)
if filename == '':
self.app.inform.emit("[WARNING_NOTCL]Export Machine Code cancelled ...")
@ -4482,6 +4503,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
elif to_file is False:
# Just for adding it to the recent files list.
self.app.file_opened.emit("cncjob", filename)
self.app.file_saved.emit("cncjob", filename)
self.app.inform.emit("[success] Saved to: " + filename)
else:

View File

@ -16,6 +16,10 @@ CAD program, and create G-Code for Isolation routing.
- fixed bug in multigeometry geometry not having the bounds in self.options and crashing the GCode generation
- fixed bug that crashed whole application in case that the GCode editor is activated on a Tool gcode that is defective.
- fixed bug in Excellon Slots milling: a value of a dict key was a string instead to be an int. A cast to integer solved it.
- fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None
- fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
- made the shell toggle shortcut key work when focused on Selected Tab; toggle units shortcut also
- changed the messages for Units COnversion
5.02.3019

329
camlib.py
View File

@ -4549,7 +4549,7 @@ class CNCjob(Geometry):
elif drillz == 0:
self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
"There will be no cut, skipping %s file" % exobj.options['name'])
return
return 'fail'
else:
self.z_cut = drillz
@ -4670,139 +4670,188 @@ class CNCjob(Geometry):
if current_platform == '64bit':
if excellon_optimization_type == 'M':
log.debug("Using OR-Tools Metaheuristic Guided Local Search drill path optimization.")
for tool in tools:
self.tool=tool
self.postdata['toolC']=exobj.tools[tool]["C"]
if exobj.drills:
for tool in tools:
self.tool=tool
self.postdata['toolC']=exobj.tools[tool]["C"]
################################################
# Create the data.
node_list = []
locations = create_data_array()
tsp_size = len(locations)
num_routes = 1 # The number of routes, which is 1 in the TSP.
# Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
depot = 0
# Create routing model.
if tsp_size > 0:
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
################################################
# Create the data.
node_list = []
locations = create_data_array()
tsp_size = len(locations)
num_routes = 1 # The number of routes, which is 1 in the TSP.
# Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
depot = 0
# Create routing model.
if tsp_size > 0:
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
# Set search time limit in milliseconds.
if float(self.app.defaults["excellon_search_time"]) != 0:
search_parameters.time_limit_ms = int(
float(self.app.defaults["excellon_search_time"]) * 1000)
# Set search time limit in milliseconds.
if float(self.app.defaults["excellon_search_time"]) != 0:
search_parameters.time_limit_ms = int(
float(self.app.defaults["excellon_search_time"]) * 1000)
else:
search_parameters.time_limit_ms = 3000
# Callback to the distance function. The callback takes two
# arguments (the from and to node indices) and returns the distance between them.
dist_between_locations = CreateDistanceCallback()
dist_callback = dist_between_locations.Distance
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
# Solve, returns a solution if any.
assignment = routing.SolveWithParameters(search_parameters)
if assignment:
# Solution cost.
log.info("Total distance: " + str(assignment.ObjectiveValue()))
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
route_number = 0
node = routing.Start(route_number)
start_node = node
while not routing.IsEnd(node):
node_list.append(node)
node = assignment.Value(routing.NextVar(node))
else:
log.warning('No solution found.')
else:
search_parameters.time_limit_ms = 3000
log.warning('Specify an instance greater than 0.')
################################################
# Callback to the distance function. The callback takes two
# arguments (the from and to node indices) and returns the distance between them.
dist_between_locations = CreateDistanceCallback()
dist_callback = dist_between_locations.Distance
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
# Only if tool has points.
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
else:
gcode += self.doformat(p.spindle_code)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
# Solve, returns a solution if any.
assignment = routing.SolveWithParameters(search_parameters)
# Drillling!
for k in node_list:
locx = locations[k][0]
locy = locations[k][1]
if assignment:
# Solution cost.
log.info("Total distance: " + str(assignment.ObjectiveValue()))
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
else:
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
"The loaded Excellon file has no drills ...")
self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
return 'fail'
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
route_number = 0
node = routing.Start(route_number)
start_node = node
while not routing.IsEnd(node):
node_list.append(node)
node = assignment.Value(routing.NextVar(node))
else:
log.warning('No solution found.')
else:
log.warning('Specify an instance greater than 0.')
################################################
# Only if tool has points.
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
else:
gcode += self.doformat(p.spindle_code)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
# Drillling!
for k in node_list:
locx = locations[k][0]
locy = locations[k][1]
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
elif excellon_optimization_type == 'B':
log.debug("Using OR-Tools Basic drill path optimization.")
for tool in tools:
self.tool=tool
self.postdata['toolC']=exobj.tools[tool]["C"]
if exobj.drills:
for tool in tools:
self.tool=tool
self.postdata['toolC']=exobj.tools[tool]["C"]
################################################
node_list = []
locations = create_data_array()
tsp_size = len(locations)
num_routes = 1 # The number of routes, which is 1 in the TSP.
################################################
node_list = []
locations = create_data_array()
tsp_size = len(locations)
num_routes = 1 # The number of routes, which is 1 in the TSP.
# Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
depot = 0
# Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
depot = 0
# Create routing model.
if tsp_size > 0:
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
# Create routing model.
if tsp_size > 0:
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
# Callback to the distance function. The callback takes two
# arguments (the from and to node indices) and returns the distance between them.
dist_between_locations = CreateDistanceCallback()
dist_callback = dist_between_locations.Distance
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
# Callback to the distance function. The callback takes two
# arguments (the from and to node indices) and returns the distance between them.
dist_between_locations = CreateDistanceCallback()
dist_callback = dist_between_locations.Distance
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
# Solve, returns a solution if any.
assignment = routing.SolveWithParameters(search_parameters)
# Solve, returns a solution if any.
assignment = routing.SolveWithParameters(search_parameters)
if assignment:
# Solution cost.
log.info("Total distance: " + str(assignment.ObjectiveValue()))
if assignment:
# Solution cost.
log.info("Total distance: " + str(assignment.ObjectiveValue()))
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
route_number = 0
node = routing.Start(route_number)
start_node = node
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
route_number = 0
node = routing.Start(route_number)
start_node = node
while not routing.IsEnd(node):
node_list.append(node)
node = assignment.Value(routing.NextVar(node))
while not routing.IsEnd(node):
node_list.append(node)
node = assignment.Value(routing.NextVar(node))
else:
log.warning('No solution found.')
else:
log.warning('No solution found.')
else:
log.warning('Specify an instance greater than 0.')
################################################
log.warning('Specify an instance greater than 0.')
################################################
# Only if tool has points.
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
else:
gcode += self.doformat(p.spindle_code)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
# Drillling!
for k in node_list:
locx = locations[k][0]
locy = locations[k][1]
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
else:
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
"The loaded Excellon file has no drills ...")
self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
return 'fail'
log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
else:
self.app.inform.emit("[ERROR_NOTCL] Wrong optimization type selected.")
return 'fail'
else:
log.debug("Using Travelling Salesman drill path optimization.")
for tool in tools:
if exobj.drills:
self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"]
# Only if tool has points.
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
@ -4812,52 +4861,23 @@ class CNCjob(Geometry):
gcode += self.doformat(p.dwell_code) # Dwell time
# Drillling!
for k in node_list:
locx = locations[k][0]
locy = locations[k][1]
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
else:
self.app.inform.emit("[ERROR_NOTCL] Wrong optimization type selected.")
return
else:
log.debug("Using Travelling Salesman drill path optimization.")
for tool in tools:
self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"]
altPoints = []
for point in points[tool]:
altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
# Only if tool has points.
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
for point in self.optimized_travelling_salesman(altPoints):
gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
gcode += self.doformat(p.down_code, x=point[0], y=point[1])
gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
self.oldx = point[0]
self.oldy = point[1]
else:
gcode += self.doformat(p.spindle_code)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
# Drillling!
altPoints = []
for point in points[tool]:
altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
for point in self.optimized_travelling_salesman(altPoints):
gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
gcode += self.doformat(p.down_code, x=point[0], y=point[1])
gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
self.oldx = point[0]
self.oldy = point[1]
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
"The loaded Excellon file has no drills ...")
self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
return 'fail'
log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
gcode += self.doformat(p.spindle_stop_code) # Spindle stop
@ -4867,6 +4887,7 @@ class CNCjob(Geometry):
log.debug("The total travel distance including travel to end position is: %s" %
str(measured_distance) + '\n')
self.gcode = gcode
return 'OK'
def generate_from_multitool_geometry(self, geometry, append=True,
tooldia=None, offset=0.0, tolerance=0, z_cut=1.0, z_move=2.0,