From d1b1a097c18fbceb7bcbcb1ac707e42c26fd1aba Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 25 Jan 2019 20:46:30 +0200 Subject: [PATCH 01/23] - fixed TclCommand Cutout - added a new TclCommand named CutoutAny. Keyword: cutout_any --- FlatCAMApp.py | 4 ++-- README.md | 4 ++++ postprocessors/grbl_11.py | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index c107caa4..afce8b52 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -90,8 +90,8 @@ class App(QtCore.QObject): log.addHandler(handler) # Version - version = 8.904 - version_date = "2019/01/25" + version = 8.905 + version_date = "2019/01/26" beta = True # URL for update checks and statistics diff --git a/README.md b/README.md index ff289806..c8f40fff 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing. ================================================= +26.01.2019 + +- fixed grbl_11 postprocessor in linear_code() function + 25.01.2019 - deleted junk folders diff --git a/postprocessors/grbl_11.py b/postprocessors/grbl_11.py index 77770912..7d2160e6 100644 --- a/postprocessors/grbl_11.py +++ b/postprocessors/grbl_11.py @@ -105,7 +105,8 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez) return ('G00 ' + self.position_code(p)).format(**p) def linear_code(self, p): - return ('G01 ' + self.position_code(p)).format(**p) + " " + self.feedrate_code(p) + return ('G01 ' + self.position_code(p)).format(**p) + \ + ' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate)) def end_code(self, p): coords_xy = p['toolchange_xy'] From 41d7ef9e2c5fcb16b38d2c87ff19789b9270b943 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 26 Jan 2019 00:12:40 +0200 Subject: [PATCH 02/23] - added icons to the Project Tab context menu - added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project) --- FlatCAMApp.py | 23 +++++++++++++++++++++++ FlatCAMGUI.py | 50 ++++++++++++++++++++++++++++++++++---------------- README.md | 2 ++ 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index afce8b52..4879282c 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -983,6 +983,10 @@ class App(QtCore.QObject): self.ui.shell_btn.triggered.connect(self.on_toggle_shell) # Context Menu + self.ui.popmenu_new_geo.triggered.connect(lambda: self.new_object('geometry', 'new_g', lambda x, y: None)) + self.ui.popmenu_new_exc.triggered.connect(self.new_excellon_object) + self.ui.popmenu_new_prj.triggered.connect(self.on_file_new) + self.ui.gridmenu_1.triggered.connect(lambda: self.ui.grid_gap_x_entry.setText("0.05")) self.ui.gridmenu_2.triggered.connect(lambda: self.ui.grid_gap_x_entry.setText("0.1")) self.ui.gridmenu_3.triggered.connect(lambda: self.ui.grid_gap_x_entry.setText("0.2")) @@ -999,6 +1003,13 @@ class App(QtCore.QObject): self.ui.zoomfit.triggered.connect(self.on_zoom_fit) self.ui.clearplot.triggered.connect(self.clear_plots) self.ui.replot.triggered.connect(self.plot_all) + + self.ui.popmenu_copy.triggered.connect(self.on_copy_object) + self.ui.popmenu_delete.triggered.connect(self.on_delete) + self.ui.popmenu_edit.triggered.connect(self.object2editor) + self.ui.popmenu_save.triggered.connect(self.editor2object) + self.ui.popmenu_move.triggered.connect(self.obj_move) + self.ui.popmenu_properties.triggered.connect(self.obj_properties) # Preferences Plot Area TAB @@ -1373,6 +1384,10 @@ class App(QtCore.QObject): :return: None """ + # adjust the visibility of some of the canvas context menu + self.ui.popmenu_edit.setVisible(False) + self.ui.popmenu_save.setVisible(True) + if isinstance(self.collection.get_active(), FlatCAMGeometry): edited_object = self.collection.get_active() # for now, if the Geometry is MultiGeo do not allow the editing @@ -1415,6 +1430,11 @@ class App(QtCore.QObject): :return: None """ + + # adjust the visibility of some of the canvas context menu + self.ui.popmenu_edit.setVisible(True) + self.ui.popmenu_save.setVisible(False) + edited_obj = self.collection.get_active() obj_type = "" @@ -4124,6 +4144,9 @@ class App(QtCore.QObject): def obj_properties(self): self.properties_tool.run() + def obj_move(self): + self.move_tool.run() + def on_fileopengerber(self): """ File menu callback for opening a Gerber. diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index b0e801ea..427d1852 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -313,21 +313,21 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuhelp_shortcut_list = self.menuhelp.addAction(QtGui.QIcon('share/shortcuts24.png'), 'Shortcuts List') self.menuhelp_videohelp = self.menuhelp.addAction(QtGui.QIcon('share/videohelp24.png'), 'See on YouTube') - #################### - ### Context menu ### - #################### + ################################ + ### Project Tab Context menu ### + ################################ self.menuproject = QtWidgets.QMenu() - self.menuprojectenable = self.menuproject.addAction('Enable') - self.menuprojectdisable = self.menuproject.addAction('Disable') + self.menuprojectenable = self.menuproject.addAction(QtGui.QIcon('share/replot32.png'), 'Enable') + self.menuprojectdisable = self.menuproject.addAction(QtGui.QIcon('share/clear_plot32.png'), 'Disable') self.menuproject.addSeparator() - self.menuprojectgeneratecnc = self.menuproject.addAction('Generate CNC') + self.menuprojectgeneratecnc = self.menuproject.addAction(QtGui.QIcon('share/cnc32.png'), 'Generate CNC') self.menuproject.addSeparator() - self.menuprojectcopy = self.menuproject.addAction('Copy') - self.menuprojectedit = self.menuproject.addAction('Edit') - self.menuprojectdelete = self.menuproject.addAction('Delete') + self.menuprojectcopy = self.menuproject.addAction(QtGui.QIcon('share/copy32.png'), 'Copy') + self.menuprojectedit = self.menuproject.addAction(QtGui.QIcon('share/edit_ok32.png'), 'Edit') + self.menuprojectdelete = self.menuproject.addAction(QtGui.QIcon('share/delete32.png'), 'Delete') self.menuproject.addSeparator() - self.menuprojectproperties = self.menuproject.addAction('Properties') + self.menuprojectproperties = self.menuproject.addAction(QtGui.QIcon('share/properties32.png'), 'Properties') ############### ### Toolbar ### @@ -625,11 +625,19 @@ class FlatCAMGUI(QtWidgets.QMainWindow): "which is the file storing the working default preferences.") self.pref_tab_bottom_layout_2.addWidget(self.pref_save_button) - ######################################## + + ############################################################## ### HERE WE BUILD THE CONTEXT MENU FOR RMB CLICK ON CANVAS ### - ######################################## + ############################################################## self.popMenu = QtWidgets.QMenu() + self.cmenu_newmenu = self.popMenu.addMenu(QtGui.QIcon('share/file32.png'), "New") + self.popmenu_new_geo = self.cmenu_newmenu.addAction(QtGui.QIcon('share/new_geo32_bis.png'), "Geo Obj") + self.popmenu_new_exc = self.cmenu_newmenu.addAction(QtGui.QIcon('share/new_exc32.png'), "Exc. Obj") + self.cmenu_newmenu.addSeparator() + self.popmenu_new_prj = self.cmenu_newmenu.addAction(QtGui.QIcon('share/file16.png'), "Project") + self.popMenu.addSeparator() + self.cmenu_gridmenu = self.popMenu.addMenu(QtGui.QIcon('share/grid32_menu.png'), "Grids") self.gridmenu_1 = self.cmenu_gridmenu.addAction(QtGui.QIcon('share/grid32_menu.png'), "0.05") self.gridmenu_2 = self.cmenu_gridmenu.addAction(QtGui.QIcon('share/grid32_menu.png'), "0.10") @@ -637,6 +645,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.gridmenu_4 = self.cmenu_gridmenu.addAction(QtGui.QIcon('share/grid32_menu.png'), "0.50") self.gridmenu_5 = self.cmenu_gridmenu.addAction(QtGui.QIcon('share/grid32_menu.png'), "1.00") + self.cmenu_viewmenu = self.popMenu.addMenu(QtGui.QIcon('share/view64.png'), "View") + self.zoomfit = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/zoom_fit32.png'), "Zoom Fit") + self.clearplot = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/clear_plot32.png'), "Clear Plot") + self.replot = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/replot32.png'), "Replot") + self.popMenu.addSeparator() + self.g_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/draw32.png'), "Geo Editor") self.draw_line = self.g_editor_cmenu.addAction(QtGui.QIcon('share/path32.png'), "Line") self.draw_rect = self.g_editor_cmenu.addAction(QtGui.QIcon('share/rectangle32.png'), "Rectangle") @@ -647,11 +661,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.drill_array = self.e_editor_cmenu.addAction(QtGui.QIcon('share/addarray32.png'), "Add Drill Array") self.drill_copy = self.e_editor_cmenu.addAction(QtGui.QIcon('share/copy32.png'), "Copy Drill(s)") - self.cmenu_viewmenu = self.popMenu.addMenu(QtGui.QIcon('share/view64.png'), "View") - self.zoomfit = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/zoom_fit32.png'), "Zoom Fit") - self.clearplot = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/clear_plot32.png'), "Clear Plot") - self.replot = self.cmenu_viewmenu.addAction(QtGui.QIcon('share/replot32.png'), "Replot") + self.popMenu.addSeparator() + self.popmenu_copy = self.popMenu.addAction(QtGui.QIcon('share/copy32.png'), "Copy") + self.popmenu_delete = self.popMenu.addAction(QtGui.QIcon('share/delete32.png'), "Delete") + self.popmenu_edit = self.popMenu.addAction(QtGui.QIcon('share/edit32.png'), "Edit") + self.popmenu_save = self.popMenu.addAction(QtGui.QIcon('share/floppy32.png'), "Save && Close Edit") + self.popmenu_save.setVisible(False) + self.popMenu.addSeparator() + self.popmenu_move = self.popMenu.addAction(QtGui.QIcon('share/move32.png'), "Move") self.popmenu_properties = self.popMenu.addAction(QtGui.QIcon('share/properties32.png'), "Properties") diff --git a/README.md b/README.md index c8f40fff..e82e9f2c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ CAD program, and create G-Code for Isolation routing. 26.01.2019 - fixed grbl_11 postprocessor in linear_code() function +- added icons to the Project Tab context menu +- added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project) 25.01.2019 From 34749cae6682737fbaaadaa765cb22652894cd7c Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 26 Jan 2019 00:26:58 +0200 Subject: [PATCH 03/23] - added icons to the Project Tab context menu - added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project) --- FlatCAMGUI.py | 6 +++--- README.md | 1 + postprocessors/grbl_laser.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 427d1852..4a8852b8 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -318,14 +318,14 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ################################ self.menuproject = QtWidgets.QMenu() - self.menuprojectenable = self.menuproject.addAction(QtGui.QIcon('share/replot32.png'), 'Enable') - self.menuprojectdisable = self.menuproject.addAction(QtGui.QIcon('share/clear_plot32.png'), 'Disable') + self.menuprojectenable = self.menuproject.addAction(QtGui.QIcon('share/replot32.png'), 'Enable Plot') + self.menuprojectdisable = self.menuproject.addAction(QtGui.QIcon('share/clear_plot32.png'), 'Disable Plot') self.menuproject.addSeparator() self.menuprojectgeneratecnc = self.menuproject.addAction(QtGui.QIcon('share/cnc32.png'), 'Generate CNC') self.menuproject.addSeparator() self.menuprojectcopy = self.menuproject.addAction(QtGui.QIcon('share/copy32.png'), 'Copy') - self.menuprojectedit = self.menuproject.addAction(QtGui.QIcon('share/edit_ok32.png'), 'Edit') self.menuprojectdelete = self.menuproject.addAction(QtGui.QIcon('share/delete32.png'), 'Delete') + self.menuprojectedit = self.menuproject.addAction(QtGui.QIcon('share/edit_ok32.png'), 'Edit') self.menuproject.addSeparator() self.menuprojectproperties = self.menuproject.addAction(QtGui.QIcon('share/properties32.png'), 'Properties') diff --git a/README.md b/README.md index e82e9f2c..34ffe01b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ CAD program, and create G-Code for Isolation routing. - fixed grbl_11 postprocessor in linear_code() function - added icons to the Project Tab context menu - added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project) +- fixed grbl_laser postprocessor file 25.01.2019 diff --git a/postprocessors/grbl_laser.py b/postprocessors/grbl_laser.py index 02af15c1..e0344ca6 100644 --- a/postprocessors/grbl_laser.py +++ b/postprocessors/grbl_laser.py @@ -52,7 +52,8 @@ class grbl_laser(FlatCAMPostProc): return ('G00 ' + self.position_code(p)).format(**p) def linear_code(self, p): - return ('G01 ' + self.position_code(p)).format(**p) + " " + self.feedrate_code(p) + return ('G01 ' + self.position_code(p)).format(**p) + \ + ' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate)) def end_code(self, p): gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n") From c0031235cd67686fb5685d88fb84a1d5c498b9a3 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 26 Jan 2019 09:08:41 +0200 Subject: [PATCH 04/23] - fixed grbl_laser postprocessor file - updated function for copy of an Excellon object for the case when the object has slots - updated FlatCAMExcellon.merge() function to work in case some (or all) of the merged objects have slots --- FlatCAMApp.py | 4 ++ FlatCAMObj.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++---- README.md | 2 + 3 files changed, 119 insertions(+), 10 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 4879282c..fed5f39c 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -3126,6 +3126,8 @@ class App(QtCore.QObject): # drills are offset, so they need to be deep copied obj_init.drills = deepcopy(obj.drills) + # slots are offset, so they need to be deep copied + obj_init.slots = deepcopy(obj.slots) obj_init.create_geometry() for obj in self.collection.get_selected(): @@ -3161,6 +3163,8 @@ class App(QtCore.QObject): obj_init.tools = obj.tools # drills are offset, so they need to be deep copied obj_init.drills = deepcopy(obj.drills) + # slots are offset, so they need to be deep copied + obj_init.slots = deepcopy(obj.slots) obj_init.create_geometry() for obj in self.collection.get_selected(): diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 6590e54d..a82ee97e 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -899,14 +899,21 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): :return: None """ + # flag to signal that we need to reorder the tools dictionary and drills and slots lists + flag_order = False + try: flattened_list = list(itertools.chain(*exc_list)) except TypeError: flattened_list = exc_list # this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict - # values will be list of Shapely Points - custom_dict = {} + # values will be list of Shapely Points; for drills + custom_dict_drills = {} + + # this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict + # values will be list of Shapely Points; for slots + custom_dict_slots = {} for exc in flattened_list: # copy options of the current excellon obj to the final excellon obj @@ -920,21 +927,29 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): for drill in exc.drills: exc_tool_dia = float('%.3f' % exc.tools[drill['tool']]['C']) - if exc_tool_dia not in custom_dict: - custom_dict[exc_tool_dia] = [drill['point']] + if exc_tool_dia not in custom_dict_drills: + custom_dict_drills[exc_tool_dia] = [drill['point']] else: - custom_dict[exc_tool_dia].append(drill['point']) + custom_dict_drills[exc_tool_dia].append(drill['point']) - # add the zeros and units to the exc_final object + for slot in exc.slots: + exc_tool_dia = float('%.3f' % exc.tools[slot['tool']]['C']) + + if exc_tool_dia not in custom_dict_slots: + custom_dict_slots[exc_tool_dia] = [[slot['start'], slot['stop']]] + else: + custom_dict_slots[exc_tool_dia].append([slot['start'], slot['stop']]) + + # add the zeros and units to the exc_final object exc_final.zeros = exc.zeros exc_final.units = exc.units # variable to make tool_name for the tools current_tool = 0 - # Here we add data to the exc_final object - # the tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points - for tool_dia in custom_dict: + # the tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points in case of + # drills + for tool_dia in custom_dict_drills: # we create a tool name for each key in the drill_dia dict (the key is a unique drill diameter) current_tool += 1 @@ -943,7 +958,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): exc_final.tools[tool_name] = spec # rebuild the drills list of dict's that belong to the exc_final object - for point in custom_dict[tool_dia]: + for point in custom_dict_drills[tool_dia]: exc_final.drills.append( { "point": point, @@ -951,6 +966,94 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): } ) + # Here we add data to the exc_final object + # the tools diameter are now the keys in the drill_dia dict and the values are a list ([start, stop]) + # of two Shapely Points in case of slots + for tool_dia in custom_dict_slots: + # we create a tool name for each key in the slot_dia dict (the key is a unique slot diameter) + # but only if there are no drills + if not exc_final.tools: + current_tool += 1 + tool_name = str(current_tool) + spec = {"C": float(tool_dia)} + exc_final.tools[tool_name] = spec + else: + dia_list = [] + for v in exc_final.tools.values(): + dia_list.append(float(v["C"])) + + if tool_dia not in dia_list: + flag_order = True + + current_tool = len(dia_list) + 1 + tool_name = str(current_tool) + spec = {"C": float(tool_dia)} + exc_final.tools[tool_name] = spec + + else: + for k, v in exc_final.tools.items(): + if v["C"] == tool_dia: + current_tool = int(k) + break + + # rebuild the slots list of dict's that belong to the exc_final object + for point in custom_dict_slots[tool_dia]: + exc_final.slots.append( + { + "start": point[0], + "stop": point[1], + "tool": str(current_tool) + } + ) + + # flag_order == True means that there was an slot diameter not in the tools and we also have drills + # and the new tool was added to self.tools therefore we need to reorder the tools and drills and slots + current_tool = 0 + if flag_order is True: + dia_list = [] + temp_drills = [] + temp_slots = [] + temp_tools = {} + for v in exc_final.tools.values(): + dia_list.append(float(v["C"])) + dia_list.sort() + for ordered_dia in dia_list: + current_tool += 1 + tool_name_temp = str(current_tool) + spec_temp = {"C": float(ordered_dia)} + temp_tools[tool_name_temp] = spec_temp + + for drill in exc_final.drills: + exc_tool_dia = float('%.3f' % exc_final.tools[drill['tool']]['C']) + if exc_tool_dia == ordered_dia: + temp_drills.append( + { + "point": drill["point"], + "tool": str(current_tool) + } + ) + + for slot in exc_final.slots: + slot_tool_dia = float('%.3f' % exc_final.tools[slot['tool']]['C']) + if slot_tool_dia == ordered_dia: + temp_slots.append( + { + "start": slot["start"], + "stop": slot["stop"], + "tool": str(current_tool) + } + ) + + # delete the exc_final tools, drills and slots + exc_final.tools = dict() + exc_final.drills[:] = [] + exc_final.slots[:] = [] + + # update the exc_final tools, drills and slots with the ordered values + exc_final.tools = temp_tools + exc_final.drills[:] = temp_drills + exc_final.slots[:] = temp_slots + # create the geometry for the exc_final object exc_final.create_geometry() diff --git a/README.md b/README.md index 34ffe01b..1e9d5413 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ CAD program, and create G-Code for Isolation routing. - added icons to the Project Tab context menu - added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project) - fixed grbl_laser postprocessor file +- updated function for copy of an Excellon object for the case when the object has slots +- updated FlatCAMExcellon.merge() function to work in case some (or all) of the merged objects have slots 25.01.2019 From c4a9c8bd774747bc8efcbfbe56d67c5c220abf68 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 03:32:09 +0200 Subject: [PATCH 05/23] - added more key shortcuts into the application; they are now displayed in the GUI menu's - reorganized the Edit -> Preferences -> Global - redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app --- FlatCAM.py | 1 + FlatCAMApp.py | 404 ++++++++++++++++++----------- FlatCAMEditor.py | 30 ++- FlatCAMGUI.py | 330 ++++++++++++++--------- FlatCAMObj.py | 6 +- FlatCAMTool.py | 9 +- ObjectCollection.py | 66 ++++- PlotCanvas.py | 4 +- README.md | 7 + flatcamTools/ToolCalculators.py | 3 + flatcamTools/ToolCutout.py | 5 +- flatcamTools/ToolDblSided.py | 5 +- flatcamTools/ToolFilm.py | 5 +- flatcamTools/ToolMeasurement.py | 11 +- flatcamTools/ToolMove.py | 2 +- flatcamTools/ToolNonCopperClear.py | 8 +- flatcamTools/ToolPaint.py | 8 +- flatcamTools/ToolPanelize.py | 5 +- flatcamTools/ToolProperties.py | 9 +- flatcamTools/ToolTransform.py | 3 + share/toggle_units16.png | Bin 0 -> 317 bytes share/toggle_units32.png | Bin 0 -> 518 bytes 22 files changed, 595 insertions(+), 326 deletions(-) create mode 100644 share/toggle_units16.png create mode 100644 share/toggle_units32.png diff --git a/FlatCAM.py b/FlatCAM.py index 92b1ad86..440181f0 100644 --- a/FlatCAM.py +++ b/FlatCAM.py @@ -6,6 +6,7 @@ from FlatCAMApp import App from multiprocessing import freeze_support import VisPyPatches + if sys.platform == "win32": # cx_freeze 'module win32' workaround import OpenGL.platform.win32 diff --git a/FlatCAMApp.py b/FlatCAMApp.py index fed5f39c..37e2c233 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -104,6 +104,7 @@ class App(QtCore.QObject): manual_url = "http://flatcam.org/manual/index.html" video_url = "https://www.youtube.com/playlist?list=PLVvP2SYRpx-AQgNlfoxw93tXUXon7G94_" + should_we_quit = True ################## ## Signals ## @@ -291,25 +292,25 @@ class App(QtCore.QObject): # when adding entries here read the comments in the method found bellow named: # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True) self.defaults_form_fields = { - "units": self.general_defaults_form.general_group.units_radio, - "global_shell_at_startup": self.general_defaults_form.general_group.shell_startup_cb, - "global_version_check": self.general_defaults_form.general_group.version_check_cb, - "global_send_stats": self.general_defaults_form.general_group.send_stats_cb, - "global_gridx": self.general_defaults_form.general_group.gridx_entry, - "global_gridy": self.general_defaults_form.general_group.gridy_entry, - "global_plot_fill": self.general_defaults_form.general_group.pf_color_entry, - "global_plot_line": self.general_defaults_form.general_group.pl_color_entry, - "global_sel_fill": self.general_defaults_form.general_group.sf_color_entry, - "global_sel_line": self.general_defaults_form.general_group.sl_color_entry, - "global_alt_sel_fill": self.general_defaults_form.general_group.alt_sf_color_entry, - "global_alt_sel_line": self.general_defaults_form.general_group.alt_sl_color_entry, - "global_draw_color": self.general_defaults_form.general_group.draw_color_entry, - "global_sel_draw_color": self.general_defaults_form.general_group.sel_draw_color_entry, - "global_pan_button": self.general_defaults_form.general_group.pan_button_radio, - "global_mselect_key": self.general_defaults_form.general_group.mselect_radio, - # "global_pan_with_space_key": self.general_defaults_form.general_group.pan_with_space_cb, - "global_workspace": self.general_defaults_form.general_group.workspace_cb, - "global_workspaceT": self.general_defaults_form.general_group.wk_cb, + "units": self.general_defaults_form.general_app_group.units_radio, + "global_shell_at_startup": self.general_defaults_form.general_app_group.shell_startup_cb, + "global_version_check": self.general_defaults_form.general_app_group.version_check_cb, + "global_send_stats": self.general_defaults_form.general_app_group.send_stats_cb, + "global_gridx": self.general_defaults_form.general_gui_group.gridx_entry, + "global_gridy": self.general_defaults_form.general_gui_group.gridy_entry, + "global_plot_fill": self.general_defaults_form.general_gui_group.pf_color_entry, + "global_plot_line": self.general_defaults_form.general_gui_group.pl_color_entry, + "global_sel_fill": self.general_defaults_form.general_gui_group.sf_color_entry, + "global_sel_line": self.general_defaults_form.general_gui_group.sl_color_entry, + "global_alt_sel_fill": self.general_defaults_form.general_gui_group.alt_sf_color_entry, + "global_alt_sel_line": self.general_defaults_form.general_gui_group.alt_sl_color_entry, + "global_draw_color": self.general_defaults_form.general_gui_group.draw_color_entry, + "global_sel_draw_color": self.general_defaults_form.general_gui_group.sel_draw_color_entry, + "global_pan_button": self.general_defaults_form.general_app_group.pan_button_radio, + "global_mselect_key": self.general_defaults_form.general_app_group.mselect_radio, + # "global_pan_with_space_key": self.general_defaults_form.general_gui_group.pan_with_space_cb, + "global_workspace": self.general_defaults_form.general_gui_group.workspace_cb, + "global_workspaceT": self.general_defaults_form.general_gui_group.wk_cb, "gerber_plot": self.gerber_defaults_form.gerber_group.plot_cb, "gerber_solid": self.gerber_defaults_form.gerber_group.solid_cb, @@ -582,9 +583,9 @@ class App(QtCore.QObject): self.cncjob_options_form = CNCJobPreferencesUI() self.options_form_fields = { - "units": self.general_options_form.general_group.units_radio, - "global_gridx": self.general_options_form.general_group.gridx_entry, - "global_gridy": self.general_options_form.general_group.gridy_entry, + "units": self.general_options_form.general_app_group.units_radio, + "global_gridx": self.general_options_form.general_gui_group.gridx_entry, + "global_gridy": self.general_options_form.general_gui_group.gridy_entry, "gerber_plot": self.gerber_options_form.gerber_group.plot_cb, "gerber_solid": self.gerber_options_form.gerber_group.solid_cb, "gerber_multicolored": self.gerber_options_form.gerber_group.multicolored_cb, @@ -758,51 +759,51 @@ class App(QtCore.QObject): ### Initialize the color box's color in Preferences -> Global -> Color # Init Plot Colors - self.general_defaults_form.general_group.pf_color_entry.set_value(self.defaults['global_plot_fill']) - self.general_defaults_form.general_group.pf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pf_color_entry.set_value(self.defaults['global_plot_fill']) + self.general_defaults_form.general_gui_group.pf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_plot_fill'])[:7]) - self.general_defaults_form.general_group.pf_color_alpha_spinner.set_value( + self.general_defaults_form.general_gui_group.pf_color_alpha_spinner.set_value( int(self.defaults['global_plot_fill'][7:9], 16)) - self.general_defaults_form.general_group.pf_color_alpha_slider.setValue( + self.general_defaults_form.general_gui_group.pf_color_alpha_slider.setValue( int(self.defaults['global_plot_fill'][7:9], 16)) - self.general_defaults_form.general_group.pl_color_entry.set_value(self.defaults['global_plot_line']) - self.general_defaults_form.general_group.pl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pl_color_entry.set_value(self.defaults['global_plot_line']) + self.general_defaults_form.general_gui_group.pl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_plot_line'])[:7]) # Init Left-Right Selection colors - self.general_defaults_form.general_group.sf_color_entry.set_value(self.defaults['global_sel_fill']) - self.general_defaults_form.general_group.sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill']) + self.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_fill'])[:7]) - self.general_defaults_form.general_group.sf_color_alpha_spinner.set_value( + self.general_defaults_form.general_gui_group.sf_color_alpha_spinner.set_value( int(self.defaults['global_sel_fill'][7:9], 16)) - self.general_defaults_form.general_group.sf_color_alpha_slider.setValue( + self.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue( int(self.defaults['global_sel_fill'][7:9], 16)) - self.general_defaults_form.general_group.sl_color_entry.set_value(self.defaults['global_sel_line']) - self.general_defaults_form.general_group.sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line']) + self.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_line'])[:7]) # Init Right-Left Selection colors - self.general_defaults_form.general_group.alt_sf_color_entry.set_value(self.defaults['global_alt_sel_fill']) - self.general_defaults_form.general_group.alt_sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(self.defaults['global_alt_sel_fill']) + self.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_alt_sel_fill'])[:7]) - self.general_defaults_form.general_group.alt_sf_color_alpha_spinner.set_value( + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value( int(self.defaults['global_sel_fill'][7:9], 16)) - self.general_defaults_form.general_group.alt_sf_color_alpha_slider.setValue( + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue( int(self.defaults['global_sel_fill'][7:9], 16)) - self.general_defaults_form.general_group.alt_sl_color_entry.set_value(self.defaults['global_alt_sel_line']) - self.general_defaults_form.general_group.alt_sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(self.defaults['global_alt_sel_line']) + self.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_alt_sel_line'])[:7]) # Init Draw color and Selection Draw Color - self.general_defaults_form.general_group.draw_color_entry.set_value(self.defaults['global_draw_color']) - self.general_defaults_form.general_group.draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.draw_color_entry.set_value(self.defaults['global_draw_color']) + self.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_draw_color'])[:7]) - self.general_defaults_form.general_group.sel_draw_color_entry.set_value(self.defaults['global_sel_draw_color']) - self.general_defaults_form.general_group.sel_draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(self.defaults['global_sel_draw_color']) + self.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_draw_color'])[:7]) #### End of Data #### @@ -917,6 +918,7 @@ class App(QtCore.QObject): self.ui.menueditorigin.triggered.connect(self.on_set_origin) self.ui.menueditjump.triggered.connect(self.on_jump_to) + self.ui.menuedittoggleunits.triggered.connect(self.on_toggle_units_click) self.ui.menueditselectall.triggered.connect(self.on_selectall) self.ui.menueditpreferences.triggered.connect(self.on_preferences) @@ -942,6 +944,7 @@ class App(QtCore.QObject): self.ui.menuview_zoom_fit.triggered.connect(self.on_zoom_fit) self.ui.menuview_zoom_in.triggered.connect(lambda: self.plotcanvas.zoom(1 / 1.5)) self.ui.menuview_zoom_out.triggered.connect(lambda: self.plotcanvas.zoom(1.5)) + self.ui.menuview_toggle_grid.triggered.connect(self.on_toggle_grid) self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis) self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_menu) @@ -995,6 +998,7 @@ class App(QtCore.QObject): self.ui.draw_line.triggered.connect(self.geo_editor.draw_tool_path) self.ui.draw_rect.triggered.connect(self.geo_editor.draw_tool_rectangle) self.ui.draw_cut.triggered.connect(self.geo_editor.cutpath) + self.ui.draw_move.triggered.connect(self.geo_editor.on_move) self.ui.drill.triggered.connect(self.exc_editor.exc_add_drill) self.ui.drill_array.triggered.connect(self.exc_editor.exc_add_drill_array) self.ui.drill_copy.triggered.connect(self.exc_editor.exc_copy_drills) @@ -1019,39 +1023,48 @@ class App(QtCore.QObject): self.ui.pref_export_button.clicked.connect(self.on_export_preferences) self.ui.pref_open_button.clicked.connect(self.on_preferences_open_folder) - self.general_options_form.general_group.units_radio.group_toggle_fn = self.on_toggle_units + ############################### + ### GUI PREFERENCES SIGNALS ### + ############################### + self.general_options_form.general_app_group.units_radio.group_toggle_fn = self.on_toggle_units + self.general_defaults_form.general_app_group.language_apply_btn.clicked.connect(self.on_language_apply) + + ############################### + ### GUI PREFERENCES SIGNALS ### + ############################### + # Setting plot colors signals - self.general_defaults_form.general_group.pf_color_entry.editingFinished.connect(self.on_pf_color_entry) - self.general_defaults_form.general_group.pf_color_button.clicked.connect(self.on_pf_color_button) - self.general_defaults_form.general_group.pf_color_alpha_spinner.valueChanged.connect(self.on_pf_color_spinner) - self.general_defaults_form.general_group.pf_color_alpha_slider.valueChanged.connect(self.on_pf_color_slider) - self.general_defaults_form.general_group.pl_color_entry.editingFinished.connect(self.on_pl_color_entry) - self.general_defaults_form.general_group.pl_color_button.clicked.connect(self.on_pl_color_button) + self.general_defaults_form.general_gui_group.pf_color_entry.editingFinished.connect(self.on_pf_color_entry) + self.general_defaults_form.general_gui_group.pf_color_button.clicked.connect(self.on_pf_color_button) + self.general_defaults_form.general_gui_group.pf_color_alpha_spinner.valueChanged.connect(self.on_pf_color_spinner) + self.general_defaults_form.general_gui_group.pf_color_alpha_slider.valueChanged.connect(self.on_pf_color_slider) + self.general_defaults_form.general_gui_group.pl_color_entry.editingFinished.connect(self.on_pl_color_entry) + self.general_defaults_form.general_gui_group.pl_color_button.clicked.connect(self.on_pl_color_button) # Setting selection (left - right) colors signals - self.general_defaults_form.general_group.sf_color_entry.editingFinished.connect(self.on_sf_color_entry) - self.general_defaults_form.general_group.sf_color_button.clicked.connect(self.on_sf_color_button) - self.general_defaults_form.general_group.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner) - self.general_defaults_form.general_group.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider) - self.general_defaults_form.general_group.sl_color_entry.editingFinished.connect(self.on_sl_color_entry) - self.general_defaults_form.general_group.sl_color_button.clicked.connect(self.on_sl_color_button) + self.general_defaults_form.general_gui_group.sf_color_entry.editingFinished.connect(self.on_sf_color_entry) + self.general_defaults_form.general_gui_group.sf_color_button.clicked.connect(self.on_sf_color_button) + self.general_defaults_form.general_gui_group.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner) + self.general_defaults_form.general_gui_group.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider) + self.general_defaults_form.general_gui_group.sl_color_entry.editingFinished.connect(self.on_sl_color_entry) + self.general_defaults_form.general_gui_group.sl_color_button.clicked.connect(self.on_sl_color_button) # Setting selection (right - left) colors signals - self.general_defaults_form.general_group.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry) - self.general_defaults_form.general_group.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button) - self.general_defaults_form.general_group.alt_sf_color_alpha_spinner.valueChanged.connect( + self.general_defaults_form.general_gui_group.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry) + self.general_defaults_form.general_gui_group.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button) + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.valueChanged.connect( self.on_alt_sf_color_spinner) - self.general_defaults_form.general_group.alt_sf_color_alpha_slider.valueChanged.connect( + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.valueChanged.connect( self.on_alt_sf_color_slider) - self.general_defaults_form.general_group.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry) - self.general_defaults_form.general_group.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button) + self.general_defaults_form.general_gui_group.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry) + self.general_defaults_form.general_gui_group.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button) # Setting Editor Draw colors signals - self.general_defaults_form.general_group.draw_color_entry.editingFinished.connect(self.on_draw_color_entry) - self.general_defaults_form.general_group.draw_color_button.clicked.connect(self.on_draw_color_button) + self.general_defaults_form.general_gui_group.draw_color_entry.editingFinished.connect(self.on_draw_color_entry) + self.general_defaults_form.general_gui_group.draw_color_button.clicked.connect(self.on_draw_color_button) - self.general_defaults_form.general_group.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry) - self.general_defaults_form.general_group.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button) + self.general_defaults_form.general_gui_group.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry) + self.general_defaults_form.general_gui_group.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button) - self.general_defaults_form.general_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified) - self.general_defaults_form.general_group.workspace_cb.stateChanged.connect(self.on_workspace) + self.general_defaults_form.general_gui_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified) + self.general_defaults_form.general_gui_group.workspace_cb.stateChanged.connect(self.on_workspace) # Modify G-CODE Plot Area TAB @@ -1067,7 +1080,7 @@ class App(QtCore.QObject): self.collection.view.activated.connect(self.on_row_activated) # Monitor the checkbox from the Application Defaults Tab and show the TCL shell or not depending on it's value - self.general_defaults_form.general_group.shell_startup_cb.clicked.connect(self.on_toggle_shell) + self.general_defaults_form.general_app_group.shell_startup_cb.clicked.connect(self.on_toggle_shell) # Load the defaults values into the Excellon Format and Excellon Zeros fields self.excellon_defaults_form.excellon_group.excellon_defaults_button.clicked.connect( @@ -1173,7 +1186,7 @@ class App(QtCore.QObject): # Separate thread (Not worker) # Check for updates on startup but only if the user consent and the app is not in Beta version if (self.beta is False or self.beta is None) and \ - self.general_defaults_form.general_group.version_check_cb.get_value() is True: + self.general_defaults_form.general_gui_group.version_check_cb.get_value() is True: App.log.info("Checking for updates in backgroud (this is version %s)." % str(self.version)) self.thr2 = QtCore.QThread() @@ -2214,13 +2227,16 @@ class App(QtCore.QObject): "Do you want to Save the project?") msgbox.setWindowTitle("Save changes") msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png')) - msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) - msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok) + msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | + QtWidgets.QMessageBox.Cancel) + msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes) response = msgbox.exec_() - if response == QtWidgets.QMessageBox.Ok: + if response == QtWidgets.QMessageBox.Yes: self.on_file_saveprojectas(thread=False) + elif response == QtWidgets.QMessageBox.Cancel: + return self.save_defaults() else: self.save_defaults() @@ -2350,14 +2366,17 @@ class App(QtCore.QObject): "Do you want to Save the project?") msgbox.setWindowTitle("Save changes") msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png')) - msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) - msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok) + msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | + QtWidgets.QMessageBox.Cancel) + msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes) response = msgbox.exec_() - if response == QtWidgets.QMessageBox.Ok: + if response == QtWidgets.QMessageBox.Yes: self.on_file_saveprojectas(thread=False) - + elif response == QtWidgets.QMessageBox.Cancel: + self.should_we_quit = False + return self.save_defaults() log.debug("Application defaults saved ... Exit event.") @@ -2508,7 +2527,7 @@ class App(QtCore.QObject): return # If option is the same, then ignore - if self.general_options_form.general_group.units_radio.get_value().upper() == self.options["units"].upper(): + if self.general_options_form.general_app_group.units_radio.get_value().upper() == self.options["units"].upper(): self.log.debug("on_toggle_units(): Same as options, so ignoring.") return @@ -2542,7 +2561,7 @@ class App(QtCore.QObject): # The scaling factor depending on choice of units. factor = 1/25.4 - if self.general_options_form.general_group.units_radio.get_value().upper() == 'MM': + if self.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM': factor = 25.4 @@ -2570,7 +2589,7 @@ class App(QtCore.QObject): self.ui.grid_gap_y_entry.set_value(float(self.ui.grid_gap_y_entry.get_value()) * factor) for obj in self.collection.get_list(): - units = self.general_options_form.general_group.units_radio.get_value().upper() + units = self.general_options_form.general_app_group.units_radio.get_value().upper() obj.convert_units(units) # make that the properties stored in the object are also updated @@ -2587,10 +2606,10 @@ class App(QtCore.QObject): else: # Undo toggling self.toggle_units_ignore = True - if self.general_options_form.general_group.units_radio.get_value().upper() == 'MM': - self.general_options_form.general_group.units_radio.set_value('IN') + if self.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM': + self.general_options_form.general_app_group.units_radio.set_value('IN') else: - self.general_options_form.general_group.units_radio.set_value('MM') + self.general_options_form.general_app_group.units_radio.set_value('MM') self.toggle_units_ignore = False self.options_read_form() @@ -2598,6 +2617,18 @@ class App(QtCore.QObject): #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': + self.general_options_form.general_app_group.units_radio.set_value("IN") + else: + self.general_options_form.general_app_group.units_radio.set_value("MM") + self.on_toggle_units() + + def on_language_apply(self): + # TODO: apply the language + # app restart section + pass + def on_toggle_axis(self): if self.toggle_axis is False: self.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0)) @@ -2611,6 +2642,9 @@ class App(QtCore.QObject): self.plotcanvas.redraw() self.toggle_axis = False + def on_toggle_grid(self): + self.ui.grid_snap_btn.trigger() + def on_options_combo_change(self, sel): """ Called when the combo box to choose between application defaults and @@ -2702,9 +2736,9 @@ class App(QtCore.QObject): # Setting plot colors handlers def on_pf_color_entry(self): - self.defaults['global_plot_fill'] = self.general_defaults_form.general_group.pf_color_entry.get_value()[:7] + \ + self.defaults['global_plot_fill'] = self.general_defaults_form.general_gui_group.pf_color_entry.get_value()[:7] + \ self.defaults['global_plot_fill'][7:9] - self.general_defaults_form.general_group.pf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_plot_fill'])[:7]) def on_pf_color_button(self): @@ -2716,29 +2750,29 @@ class App(QtCore.QObject): if plot_fill_color.isValid() is False: return - self.general_defaults_form.general_group.pf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pf_color_button.setStyleSheet( "background-color:%s" % str(plot_fill_color.name())) new_val = str(plot_fill_color.name()) + str(self.defaults['global_plot_fill'][7:9]) - self.general_defaults_form.general_group.pf_color_entry.set_value(new_val) + self.general_defaults_form.general_gui_group.pf_color_entry.set_value(new_val) self.defaults['global_plot_fill'] = new_val def on_pf_color_spinner(self): - spinner_value = self.general_defaults_form.general_group.pf_color_alpha_spinner.value() - self.general_defaults_form.general_group.pf_color_alpha_slider.setValue(spinner_value) + spinner_value = self.general_defaults_form.general_gui_group.pf_color_alpha_spinner.value() + self.general_defaults_form.general_gui_group.pf_color_alpha_slider.setValue(spinner_value) self.defaults['global_plot_fill'] = self.defaults['global_plot_fill'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') self.defaults['global_plot_line'] = self.defaults['global_plot_line'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') def on_pf_color_slider(self): - slider_value = self.general_defaults_form.general_group.pf_color_alpha_slider.value() - self.general_defaults_form.general_group.pf_color_alpha_spinner.setValue(slider_value) + slider_value = self.general_defaults_form.general_gui_group.pf_color_alpha_slider.value() + self.general_defaults_form.general_gui_group.pf_color_alpha_spinner.setValue(slider_value) def on_pl_color_entry(self): - self.defaults['global_plot_line'] = self.general_defaults_form.general_group.pl_color_entry.get_value()[:7] + \ + self.defaults['global_plot_line'] = self.general_defaults_form.general_gui_group.pl_color_entry.get_value()[:7] + \ self.defaults['global_plot_line'][7:9] - self.general_defaults_form.general_group.pl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_plot_line'])[:7]) def on_pl_color_button(self): @@ -2751,18 +2785,18 @@ class App(QtCore.QObject): if plot_line_color.isValid() is False: return - self.general_defaults_form.general_group.pl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.pl_color_button.setStyleSheet( "background-color:%s" % str(plot_line_color.name())) new_val_line = str(plot_line_color.name()) + str(self.defaults['global_plot_line'][7:9]) - self.general_defaults_form.general_group.pl_color_entry.set_value(new_val_line) + self.general_defaults_form.general_gui_group.pl_color_entry.set_value(new_val_line) self.defaults['global_plot_line'] = new_val_line # Setting selection colors (left - right) handlers def on_sf_color_entry(self): - self.defaults['global_sel_fill'] = self.general_defaults_form.general_group.sf_color_entry.get_value()[:7] + \ + self.defaults['global_sel_fill'] = self.general_defaults_form.general_gui_group.sf_color_entry.get_value()[:7] + \ self.defaults['global_sel_fill'][7:9] - self.general_defaults_form.general_group.sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_fill'])[:7]) def on_sf_color_button(self): @@ -2774,29 +2808,29 @@ class App(QtCore.QObject): if plot_fill_color.isValid() is False: return - self.general_defaults_form.general_group.sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet( "background-color:%s" % str(plot_fill_color.name())) new_val = str(plot_fill_color.name()) + str(self.defaults['global_sel_fill'][7:9]) - self.general_defaults_form.general_group.sf_color_entry.set_value(new_val) + self.general_defaults_form.general_gui_group.sf_color_entry.set_value(new_val) self.defaults['global_sel_fill'] = new_val def on_sf_color_spinner(self): - spinner_value = self.general_defaults_form.general_group.sf_color_alpha_spinner.value() - self.general_defaults_form.general_group.sf_color_alpha_slider.setValue(spinner_value) + spinner_value = self.general_defaults_form.general_gui_group.sf_color_alpha_spinner.value() + self.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue(spinner_value) self.defaults['global_sel_fill'] = self.defaults['global_sel_fill'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') self.defaults['global_sel_line'] = self.defaults['global_sel_line'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') def on_sf_color_slider(self): - slider_value = self.general_defaults_form.general_group.sf_color_alpha_slider.value() - self.general_defaults_form.general_group.sf_color_alpha_spinner.setValue(slider_value) + slider_value = self.general_defaults_form.general_gui_group.sf_color_alpha_slider.value() + self.general_defaults_form.general_gui_group.sf_color_alpha_spinner.setValue(slider_value) def on_sl_color_entry(self): - self.defaults['global_sel_line'] = self.general_defaults_form.general_group.sl_color_entry.get_value()[:7] + \ + self.defaults['global_sel_line'] = self.general_defaults_form.general_gui_group.sl_color_entry.get_value()[:7] + \ self.defaults['global_sel_line'][7:9] - self.general_defaults_form.general_group.sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_line'])[:7]) def on_sl_color_button(self): @@ -2808,18 +2842,18 @@ class App(QtCore.QObject): if plot_line_color.isValid() is False: return - self.general_defaults_form.general_group.sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet( "background-color:%s" % str(plot_line_color.name())) new_val_line = str(plot_line_color.name()) + str(self.defaults['global_sel_line'][7:9]) - self.general_defaults_form.general_group.sl_color_entry.set_value(new_val_line) + self.general_defaults_form.general_gui_group.sl_color_entry.set_value(new_val_line) self.defaults['global_sel_line'] = new_val_line # Setting selection colors (right - left) handlers def on_alt_sf_color_entry(self): - self.defaults['global_alt_sel_fill'] = self.general_defaults_form.general_group \ + self.defaults['global_alt_sel_fill'] = self.general_defaults_form.general_gui_group \ .alt_sf_color_entry.get_value()[:7] + self.defaults['global_alt_sel_fill'][7:9] - self.general_defaults_form.general_group.alt_sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_alt_sel_fill'])[:7]) def on_alt_sf_color_button(self): @@ -2831,29 +2865,29 @@ class App(QtCore.QObject): if plot_fill_color.isValid() is False: return - self.general_defaults_form.general_group.alt_sf_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet( "background-color:%s" % str(plot_fill_color.name())) new_val = str(plot_fill_color.name()) + str(self.defaults['global_alt_sel_fill'][7:9]) - self.general_defaults_form.general_group.alt_sf_color_entry.set_value(new_val) + self.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(new_val) self.defaults['global_alt_sel_fill'] = new_val def on_alt_sf_color_spinner(self): - spinner_value = self.general_defaults_form.general_group.alt_sf_color_alpha_spinner.value() - self.general_defaults_form.general_group.alt_sf_color_alpha_slider.setValue(spinner_value) + spinner_value = self.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.value() + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(spinner_value) self.defaults['global_alt_sel_fill'] = self.defaults['global_alt_sel_fill'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') self.defaults['global_alt_sel_line'] = self.defaults['global_alt_sel_line'][:7] + \ (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00') def on_alt_sf_color_slider(self): - slider_value = self.general_defaults_form.general_group.alt_sf_color_alpha_slider.value() - self.general_defaults_form.general_group.alt_sf_color_alpha_spinner.setValue(slider_value) + slider_value = self.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.value() + self.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.setValue(slider_value) def on_alt_sl_color_entry(self): - self.defaults['global_alt_sel_line'] = self.general_defaults_form.general_group \ + self.defaults['global_alt_sel_line'] = self.general_defaults_form.general_gui_group \ .alt_sl_color_entry.get_value()[:7] + self.defaults['global_alt_sel_line'][7:9] - self.general_defaults_form.general_group.alt_sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_alt_sel_line'])[:7]) def on_alt_sl_color_button(self): @@ -2865,18 +2899,18 @@ class App(QtCore.QObject): if plot_line_color.isValid() is False: return - self.general_defaults_form.general_group.alt_sl_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet( "background-color:%s" % str(plot_line_color.name())) new_val_line = str(plot_line_color.name()) + str(self.defaults['global_alt_sel_line'][7:9]) - self.general_defaults_form.general_group.alt_sl_color_entry.set_value(new_val_line) + self.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(new_val_line) self.defaults['global_alt_sel_line'] = new_val_line # Setting Editor colors def on_draw_color_entry(self): - self.defaults['global_draw_color'] = self.general_defaults_form.general_group \ + self.defaults['global_draw_color'] = self.general_defaults_form.general_gui_group \ .draw_color_entry.get_value() - self.general_defaults_form.general_group.draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_draw_color'])) def on_draw_color_button(self): @@ -2888,17 +2922,17 @@ class App(QtCore.QObject): if draw_color.isValid() is False: return - self.general_defaults_form.general_group.draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet( "background-color:%s" % str(draw_color.name())) new_val = str(draw_color.name()) - self.general_defaults_form.general_group.draw_color_entry.set_value(new_val) + self.general_defaults_form.general_gui_group.draw_color_entry.set_value(new_val) self.defaults['global_draw_color'] = new_val def on_sel_draw_color_entry(self): - self.defaults['global_sel_draw_color'] = self.general_defaults_form.general_group \ + self.defaults['global_sel_draw_color'] = self.general_defaults_form.general_gui_group \ .sel_draw_color_entry.get_value() - self.general_defaults_form.general_group.sel_draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet( "background-color:%s" % str(self.defaults['global_sel_draw_color'])) def on_sel_draw_color_button(self): @@ -2910,11 +2944,11 @@ class App(QtCore.QObject): if sel_draw_color.isValid() is False: return - self.general_defaults_form.general_group.sel_draw_color_button.setStyleSheet( + self.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet( "background-color:%s" % str(sel_draw_color.name())) new_val_sel = str(sel_draw_color.name()) - self.general_defaults_form.general_group.sel_draw_color_entry.set_value(new_val_sel) + self.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(new_val_sel) self.defaults['global_sel_draw_color'] = new_val_sel def on_workspace_modified(self): @@ -2922,7 +2956,7 @@ class App(QtCore.QObject): self.plotcanvas.draw_workspace() def on_workspace(self): - if self.general_defaults_form.general_group.workspace_cb.isChecked(): + if self.general_defaults_form.general_gui_group.workspace_cb.isChecked(): self.plotcanvas.restore_workspace() else: self.plotcanvas.delete_workspace() @@ -2930,10 +2964,10 @@ class App(QtCore.QObject): self.save_defaults(silent=True) def on_workspace_menu(self): - if self.general_defaults_form.general_group.workspace_cb.isChecked(): - self.general_defaults_form.general_group.workspace_cb.setChecked(False) + if self.general_defaults_form.general_gui_group.workspace_cb.isChecked(): + self.general_defaults_form.general_gui_group.workspace_cb.setChecked(False) else: - self.general_defaults_form.general_group.workspace_cb.setChecked(True) + self.general_defaults_form.general_gui_group.workspace_cb.setChecked(True) self.on_workspace() def on_save_button(self): @@ -3573,6 +3607,9 @@ class App(QtCore.QObject): if event.key == 'G': self.on_fileopengerber() + if event.key == 'N': + self.on_file_new_click() + if event.key == 'M': self.measurement_tool.run() @@ -3585,14 +3622,44 @@ class App(QtCore.QObject): return elif self.key_modifiers == QtCore.Qt.AltModifier: # place holder for further shortcut key - return + if event.key == 'C': + self.calculator_tool.run() + + if event.key == 'D': + self.dblsidedtool.run() + + if event.key == 'L': + self.film_tool.run() + + if event.key == 'N': + self.ncclear_tool.run() + + if event.key == 'P': + self.paint_tool.run() + + if event.key == 'R': + self.transform_tool.run() + + if event.key == 'U': + self.cutout_tool.run() + + if event.key == 'Z': + self.panelize_tool.run() + elif self.key_modifiers == QtCore.Qt.ShiftModifier: # place holder for further shortcut key + if event.key == 'C': + self.on_copy_name() + # Toggle axis if event.key == 'G': self.on_toggle_axis() + # Open Preferences Window + if event.key == 'P': + self.on_preferences() + # Rotate Object by 90 degree CCW if event.key == 'R': self.on_rotate(silent=True, preset=-90) @@ -3601,6 +3668,14 @@ class App(QtCore.QObject): if event.key == 'W': self.on_workspace_menu() + # Skew on X axis + if event.key == 'X': + self.on_skewx() + + # Skew on Y axis + if event.key == 'Y': + self.on_skewy() + return else: if event.key == self.defaults['fit_key']: # 1 @@ -3624,9 +3699,6 @@ class App(QtCore.QObject): self.collection.get_active().ui.plot_cb.toggle() self.delete_selection_shape() - if event.key == 'C': - self.on_copy_name() - if event.key == 'E': self.object2editor() @@ -3643,12 +3715,14 @@ class App(QtCore.QObject): if event.key == 'N': self.on_new_geometry() + if event.key == 'O': + self.on_set_origin() + + if event.key == 'P': + self.properties_tool.run() + if event.key == 'Q': - if self.options["units"] == 'MM': - self.general_options_form.general_group.units_radio.set_value("IN") - else: - self.general_options_form.general_group.units_radio.set_value("MM") - self.on_toggle_units() + self.on_toggle_units_click() if event.key == 'R': self.on_rotate(silent=True, preset=90) @@ -3656,9 +3730,6 @@ class App(QtCore.QObject): if event.key == 'S': self.on_toggle_shell() - if event.key == 'T': - self.transform_tool.run() - if event.key == 'V': self.on_zoom_fit(None) @@ -3695,7 +3766,6 @@ class App(QtCore.QObject): 2: Zoom Out
3: Zoom In
A: Draw an Arc (when in Edit Mode)
-C: Copy Obj_Name
C: Copy Geo Item (when in Edit Mode)
E: Edit Object (if selected)
G: Grid On/Off
@@ -3704,13 +3774,14 @@ class App(QtCore.QObject): M: Move Geo Item (when in Edit Mode)
N: New Geometry
N: Draw a Polygon (when in Edit Mode)
+O: Set Origin
O: Draw a Circle (when in Edit Mode)
Q: Change Units
+P: Open Properties Tool
P: Draw a Path (when in Edit Mode)
R: Rotate by 90 degree CW
R: Draw Rectangle (when in Edit Mode)
S: Shell Toggle
-T: Transformation
V: View Fit
X: Flip on X_axis
Y: Flip on Y_axis
@@ -3720,14 +3791,28 @@ class App(QtCore.QObject): CTRL+C: Copy Obj
CTRL+E: Open Excellon File
CTRL+G: Open Gerber File
+CTRL+N: New Project
CTRL+M: Measurement Tool
CTRL+O: Open Project
CTRL+S: Save Project As
CTRL+S: Save Object and Exit Editor (when in Edit Mode)

+SHIFT+C: Copy Obj_Name
SHIFT+G: Toggle the axis
+SHIFT+P: Open Preferences Window
SHIFT+R: Rotate by 90 degree CCW
SHIFT+W: Toggle the workspace
+SHIFT+X: Skew on X axis
+SHIFT+Y: Skew on Y axis
+
+ALT+C: Calculators Tool
+ALT+D: 2-Sided PCB Tool
+ALT+L: Film PCB Tool
+ALT+N: Non-Copper Clearing Tool
+ALT+P: Paint Area Tool
+ALT+R: Transformation Tool
+ALT+U: Cutout PCB Tool
+ALT+Z: Panelize PCB Tool
Del: Delete Obj''' helpbox = QtWidgets.QMessageBox() @@ -3740,7 +3825,13 @@ class App(QtCore.QObject): def on_copy_name(self): obj = self.collection.get_active() - name = obj.options["name"] + try: + name = obj.options["name"] + except AttributeError: + log.debug("on_copy_name() --> No object selected to copy it's name") + self.inform.emit("[warning_notcl]No object selected to copy it's name") + return + self.clipboard.setText(name) self.inform.emit("Name copied on clipboard ...") @@ -4093,13 +4184,16 @@ class App(QtCore.QObject): "Do you want to Save the project?") msgbox.setWindowTitle("Save changes") msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png')) - msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) + msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Yes | + QtWidgets.QMessageBox.No) msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok) response = msgbox.exec_() - if response == QtWidgets.QMessageBox.Ok: + if response == QtWidgets.QMessageBox.Yes: self.on_file_saveprojectas() + elif response == QtWidgets.QMessageBox.Cancel: + return self.on_file_new() else: self.on_file_new() @@ -4963,7 +5057,7 @@ class App(QtCore.QObject): return "Could not retrieve object: %s" % obj_name # updated units - units = self.general_options_form.general_group.units_radio.get_value().upper() + units = self.general_options_form.general_app_group.units_radio.get_value().upper() if units == 'IN' or units == 'INCH': units = 'INCH' @@ -5056,7 +5150,7 @@ class App(QtCore.QObject): return "Could not retrieve object: %s" % obj_name # updated units - units = self.general_options_form.general_group.units_radio.get_value().upper() + units = self.general_options_form.general_app_group.units_radio.get_value().upper() if units == 'IN' or units == 'INCH': units = 'INCH' elif units == 'MM' or units == 'METIRC': @@ -5109,7 +5203,7 @@ class App(QtCore.QObject): "Only Geometry and Gerber are supported") return - units = self.general_options_form.general_group.units_radio.get_value().upper() + units = self.general_options_form.general_app_group.units_radio.get_value().upper() def obj_init(geo_obj, app_obj): geo_obj.import_svg(filename, obj_type, units=units) @@ -5150,7 +5244,7 @@ class App(QtCore.QObject): "Only Geometry and Gerber are supported") return - units = self.general_options_form.general_group.units_radio.get_value().upper() + units = self.general_options_form.general_app_group.units_radio.get_value().upper() def obj_init(geo_obj, app_obj): geo_obj.import_dxf(filename, obj_type, units=units) @@ -5197,7 +5291,7 @@ class App(QtCore.QObject): # Object name name = outname or filename.split('/')[-1].split('\\')[-1] - units = self.general_options_form.general_group.units_radio.get_value() + units = self.general_options_form.general_app_group.units_radio.get_value() self.new_object(obj_type, name, obj_init) self.progress.emit(20) @@ -5886,7 +5980,7 @@ class App(QtCore.QObject): self.log.debug("version_check()") - if self.general_defaults_form.general_group.send_stats_cb.get_value() is True: + if self.general_defaults_form.general_gui_group.send_stats_cb.get_value() is True: full_url = App.version_url + \ "?s=" + str(self.defaults['global_serial']) + \ "&v=" + str(self.version) + \ diff --git a/FlatCAMEditor.py b/FlatCAMEditor.py index bc3264a9..e497db5d 100644 --- a/FlatCAMEditor.py +++ b/FlatCAMEditor.py @@ -291,7 +291,7 @@ class TextInputTool(FlatCAMTool): font_name=self.font_name, font_size=font_to_geo_size, font_type=font_to_geo_type, - units=self.app.general_options_form.general_group.units_radio.get_value().upper()) + units=self.app.general_options_form.general_app_group.units_radio.get_value().upper()) def font_family(self, font): self.text_input_entry.selectAll() @@ -1143,6 +1143,7 @@ class FCMove(FCShapeTool): self.start_msg = "Click on reference point." def set_origin(self, origin): + self.draw_app.app.inform.emit("Click on destination point.") self.origin = origin def click(self, point): @@ -1901,6 +1902,9 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.geo_cutpath_btn.triggered.connect(self.cutpath) self.app.ui.geo_delete_btn.triggered.connect(self.on_delete_btn) + self.app.ui.geo_move_menuitem.triggered.connect(self.on_move) + self.app.ui.geo_cornersnap_menuitem.triggered.connect(self.on_corner_snap) + ## Toolbar events and properties self.tools = { "select": {"button": self.app.ui.geo_select_btn, @@ -2596,14 +2600,11 @@ class FlatCAMGeoEditor(QtCore.QObject): # Corner Snap if event.key.name == 'K': - self.app.ui.corner_snap_btn.trigger() + self.on_corner_snap() # Move if event.key.name == 'M': - self.app.ui.geo_move_btn.setChecked(True) - self.on_tool_select('move') - self.active_tool.set_origin(self.snap(self.x, self.y)) - self.app.inform.emit("Click on target point.") + self.on_move_click() # Polygon Tool if event.key.name == 'N': @@ -2718,6 +2719,17 @@ class FlatCAMGeoEditor(QtCore.QObject): if shape in self.selected: self.selected.remove(shape) # TODO: Check performance + def on_move(self): + self.app.ui.geo_move_btn.setChecked(True) + self.on_tool_select('move') + + def on_move_click(self): + self.on_move() + self.active_tool.set_origin(self.snap(self.x, self.y)) + + def on_corner_snap(self): + self.app.ui.corner_snap_btn.trigger() + def get_selected(self): """ Returns list of shapes that are selected in the editor. @@ -3638,7 +3650,7 @@ class FlatCAMExcEditor(QtCore.QObject): self.move_timer.setSingleShot(True) ## Current application units in Upper Case - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() self.key = None # Currently pressed key self.modifiers = None @@ -3712,7 +3724,7 @@ class FlatCAMExcEditor(QtCore.QObject): def set_ui(self): # updated units - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() self.olddia_newdia.clear() self.tool2tooldia.clear() @@ -3752,7 +3764,7 @@ class FlatCAMExcEditor(QtCore.QObject): pass # updated units - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() # make a new name for the new Excellon object (the one with edited content) self.edited_obj_name = self.exc_obj.options['name'] diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 4a8852b8..d0b19797 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -36,22 +36,24 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufile = self.menu.addMenu('&File') # New - self.menufilenew = QtWidgets.QAction(QtGui.QIcon('share/file16.png'), '&New Project', self) + self.menufilenew = QtWidgets.QAction(QtGui.QIcon('share/file16.png'), '&New Project ...\tCTRL+N', self) self.menufile.addAction(self.menufilenew) self.menufile_open = self.menufile.addMenu(QtGui.QIcon('share/folder32_bis.png'), 'Open') # Open gerber ... - self.menufileopengerber = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), 'Open &Gerber ...', self) + self.menufileopengerber = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), + 'Open &Gerber ...\tCTRL+G', self) self.menufile_open.addAction(self.menufileopengerber) # Open gerber with follow... self.menufileopengerber_follow = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), - 'Open &Gerber (w/ Follow)', self) + 'Open &Gerber (w/ Follow) ...', self) self.menufile_open.addAction(self.menufileopengerber_follow) self.menufile_open.addSeparator() # Open Excellon ... - self.menufileopenexcellon = QtWidgets.QAction(QtGui.QIcon('share/open_excellon32.png'), 'Open &Excellon ...', + self.menufileopenexcellon = QtWidgets.QAction(QtGui.QIcon('share/open_excellon32.png'), + 'Open &Excellon ...\tCTRL+E', self) self.menufile_open.addAction(self.menufileopenexcellon) @@ -70,7 +72,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufile.addSeparator() # Run Scripts - self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), 'Run Script', self) + self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), 'Run Script ...', self) self.menufile.addAction(self.menufilerunscript) # Separator @@ -79,18 +81,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # Import ... self.menufileimport = self.menufile.addMenu(QtGui.QIcon('share/import.png'), 'Import') self.menufileimportsvg = QtWidgets.QAction(QtGui.QIcon('share/svg16.png'), - '&SVG as Geometry Object', self) + '&SVG as Geometry Object ...', self) self.menufileimport.addAction(self.menufileimportsvg) self.menufileimportsvg_as_gerber = QtWidgets.QAction(QtGui.QIcon('share/svg16.png'), - '&SVG as Gerber Object', self) + '&SVG as Gerber Object ...', self) self.menufileimport.addAction(self.menufileimportsvg_as_gerber) self.menufileimport.addSeparator() self.menufileimportdxf = QtWidgets.QAction(QtGui.QIcon('share/dxf16.png'), - '&DXF as Geometry Object', self) + '&DXF as Geometry Object ...', self) self.menufileimport.addAction(self.menufileimportdxf) self.menufileimportdxf_as_gerber = QtWidgets.QAction(QtGui.QIcon('share/dxf16.png'), - '&DXF as Gerber Object', self) + '&DXF as Gerber Object ...', self) self.menufileimport.addAction(self.menufileimportdxf_as_gerber) self.menufileimport.addSeparator() @@ -128,11 +130,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufile_save = self.menufile.addMenu(QtGui.QIcon('share/save_as.png'), 'Save') # Save Project - self.menufilesaveproject = QtWidgets.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project', self) + self.menufilesaveproject = QtWidgets.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project ...', self) self.menufile_save.addAction(self.menufilesaveproject) # Save Project As ... - self.menufilesaveprojectas = QtWidgets.QAction(QtGui.QIcon('share/save_as.png'), 'Save Project &As ...', self) + self.menufilesaveprojectas = QtWidgets.QAction(QtGui.QIcon('share/save_as.png'), + 'Save Project &As ...\tCTRL+S', self) self.menufile_save.addAction(self.menufilesaveprojectas) # Save Project Copy ... @@ -151,12 +154,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ### Edit ### self.menuedit = self.menu.addMenu('&Edit') - self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), '&New Geometry') - self.menueditnewexc = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Excellon') + self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), '&New Geometry\tN') + self.menueditnewexc = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Excellon\tX') # Separator self.menuedit.addSeparator() - self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Object') - self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), '&Update Object') + self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Object\tE') + self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), '&Update Object\tCTRL+S') # Separator self.menuedit.addSeparator() self.menuedit_convert = self.menuedit.addMenu(QtGui.QIcon('share/convert24.png'), 'Conversion') @@ -185,29 +188,31 @@ class FlatCAMGUI(QtWidgets.QMainWindow): "Will convert a Geometry object from multi_geometry type\n" "to a single_geometry type.") self.menuedit_convert.setToolTipsVisible(True) - # Separator - self.menuedit.addSeparator() - self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), '&Delete') # Separator self.menuedit.addSeparator() - self.menueditcopyobject = self.menuedit.addAction(QtGui.QIcon('share/copy.png'), '&Copy Object') + self.menueditcopyobject = self.menuedit.addAction(QtGui.QIcon('share/copy.png'), '&Copy Object\tCTRL+C') self.menueditcopyobjectasgeom = self.menuedit.addAction(QtGui.QIcon('share/copy_geo.png'), 'Copy as &Geom') + # Separator + self.menuedit.addSeparator() + self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), '&Delete\tDEL') # Separator self.menuedit.addSeparator() - self.menueditorigin = self.menuedit.addAction(QtGui.QIcon('share/origin.png'), 'Se&t Origin') - self.menueditjump = self.menuedit.addAction(QtGui.QIcon('share/jump_to16.png'), 'Jump to Location') + self.menueditorigin = self.menuedit.addAction(QtGui.QIcon('share/origin.png'), 'Se&t Origin\tO') + self.menueditjump = self.menuedit.addAction(QtGui.QIcon('share/jump_to16.png'), 'Jump to Location\tJ') # Separator self.menuedit.addSeparator() + self.menuedittoggleunits= self.menuedit.addAction(QtGui.QIcon('share/toggle_units16.png'), + 'Toggle Units\tQ') self.menueditselectall = self.menuedit.addAction(QtGui.QIcon('share/select_all.png'), - '&Select All') + '&Select All\tCTRL+A') # Separator self.menuedit.addSeparator() - self.menueditpreferences = self.menuedit.addAction(QtGui.QIcon('share/pref.png'), '&Preferences') + self.menueditpreferences = self.menuedit.addAction(QtGui.QIcon('share/pref.png'), '&Preferences\tSHIFT+P') ### Options ### self.menuoptions = self.menu.addMenu('&Options') @@ -225,21 +230,21 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # self.menuoptions_transform = self.menuoptions.addMenu(QtGui.QIcon('share/transform.png'), # '&Transform Object') self.menuoptions_transform_rotate = self.menuoptions.addAction(QtGui.QIcon('share/rotate.png'), - "&Rotate Selection") + "&Rotate Selection\tSHIFT+(R)") # Separator self.menuoptions.addSeparator() self.menuoptions_transform_skewx = self.menuoptions.addAction(QtGui.QIcon('share/skewX.png'), - "&Skew on X axis") + "&Skew on X axis\tSHIFT+X") self.menuoptions_transform_skewy = self.menuoptions.addAction(QtGui.QIcon('share/skewY.png'), - "S&kew on Y axis") + "S&kew on Y axis\tSHIFT+Y") # Separator self.menuoptions.addSeparator() self.menuoptions_transform_flipx = self.menuoptions.addAction(QtGui.QIcon('share/flipx.png'), - "Flip on &X axis") + "Flip on &X axis\tX") self.menuoptions_transform_flipy = self.menuoptions.addAction(QtGui.QIcon('share/flipy.png'), - "Flip on &Y axis") + "Flip on &Y axis\tY") # Separator self.menuoptions.addSeparator() @@ -252,14 +257,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow): 'Disable non-selected') # Separator self.menuview.addSeparator() - self.menuview_zoom_fit = self.menuview.addAction(QtGui.QIcon('share/zoom_fit32.png'), "&Zoom Fit") - self.menuview_zoom_in = self.menuview.addAction(QtGui.QIcon('share/zoom_in32.png'), "&Zoom In") - self.menuview_zoom_out = self.menuview.addAction(QtGui.QIcon('share/zoom_out32.png'), "&Zoom Out") + self.menuview_zoom_fit = self.menuview.addAction(QtGui.QIcon('share/zoom_fit32.png'), "&Zoom Fit\t1") + self.menuview_zoom_in = self.menuview.addAction(QtGui.QIcon('share/zoom_in32.png'), "&Zoom In\t2") + self.menuview_zoom_out = self.menuview.addAction(QtGui.QIcon('share/zoom_out32.png'), "&Zoom Out\t3") self.menuview.addSeparator() - self.menuview_toggle_axis = self.menuview.addAction(QtGui.QIcon('share/axis32.png'), "&Toggle Axis") + self.menuview_toggle_grid = self.menuview.addAction(QtGui.QIcon('share/grid32.png'), "&Toggle Grid\tG") + self.menuview_toggle_axis = self.menuview.addAction(QtGui.QIcon('share/axis32.png'), "&Toggle Axis\tSHIFT+G") self.menuview_toggle_workspace = self.menuview.addAction(QtGui.QIcon('share/workspace24.png'), - "Toggle Workspace") + "Toggle Workspace\tSHIFT+W") ### FlatCAM Editor menu ### @@ -269,28 +275,46 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menu.addMenu(self.geo_editor_menu) # self.select_menuitem = self.menu.addAction(QtGui.QIcon('share/pointer16.png'), "Select 'Esc'") - self.geo_add_circle_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/circle32.png'), 'Add Circle') - self.geo_add_arc_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/arc16.png'), 'Add Arc') + self.geo_add_circle_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/circle32.png'), 'Add Circle\tO' + ) + self.geo_add_arc_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/arc16.png'), 'Add Arc\tA') self.geo_editor_menu.addSeparator() - self.geo_add_rectangle_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/rectangle32.png'), 'Add Rectangle') - self.geo_add_polygon_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/polygon32.png'), 'Add Polygon') - self.geo_add_path_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/path32.png'), 'Add Path') + self.geo_add_rectangle_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/rectangle32.png'), 'Add Rectangle\tR' + ) + self.geo_add_polygon_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/polygon32.png'), 'Add Polygon\tN' + ) + self.geo_add_path_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/path32.png'), 'Add Path\tP') self.geo_editor_menu.addSeparator() - self.geo_add_text_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/text32.png'), 'Add Text') + self.geo_add_text_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/text32.png'), 'Add Text\tT') self.geo_editor_menu.addSeparator() self.geo_union_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/union16.png'), 'Polygon Union') self.geo_intersection_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/intersection16.png'), 'Polygon Intersection') - self.geo_subtract_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction') + self.geo_subtract_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction' + ) self.geo_editor_menu.addSeparator() - self.geo_cutpath_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/cutpath16.png'), 'Cut Path') + self.geo_cutpath_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/cutpath16.png'), 'Cut Path\tX') # self.move_menuitem = self.menu.addAction(QtGui.QIcon('share/move16.png'), "Move Objects 'm'") - self.geo_copy_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/copy16.png'), "Copy Geom") - self.geo_delete_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/deleteshape16.png'), "Delete Shape") + self.geo_copy_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/copy16.png'), "Copy Geom\tC") + self.geo_delete_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/deleteshape16.png'), "Delete Shape\tDEL" + ) self.geo_editor_menu.addSeparator() - self.geo_buffer_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/buffer16.png'), "Buffer Selection") - self.geo_paint_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/paint16.png'), "Paint Selection") + self.geo_move_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/move32.png'), "Move\tM") + self.geo_buffer_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/buffer16.png'), "Buffer Selection\tB" + ) + self.geo_paint_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/paint16.png'), "Paint Selection\tI" + ) self.geo_editor_menu.addSeparator() + self.geo_cornersnap_menuitem = self.geo_editor_menu.addAction( + QtGui.QIcon('share/corner32.png'), "Toggle Corner Snap\tK" + ) # self.exc_editor_menu = QtWidgets.QMenu("Excellon Editor") # self.menu.addMenu(self.exc_editor_menu) @@ -302,7 +326,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # self.menutool = self.menu.addMenu('&Tool') self.menutool = QtWidgets.QMenu('&Tool') self.menutoolaction = self.menu.addMenu(self.menutool) - self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line') + self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line\tS') ### Help ### self.menuhelp = self.menu.addMenu('&Help') @@ -655,6 +679,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.draw_line = self.g_editor_cmenu.addAction(QtGui.QIcon('share/path32.png'), "Line") self.draw_rect = self.g_editor_cmenu.addAction(QtGui.QIcon('share/rectangle32.png'), "Rectangle") self.draw_cut = self.g_editor_cmenu.addAction(QtGui.QIcon('share/cutpath32.png'), "Cut") + self.g_editor_cmenu.addSeparator() + self.draw_move = self.g_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), "Move") self.e_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/drill32.png'), "Exc Editor") self.drill = self.e_editor_cmenu.addAction(QtGui.QIcon('share/drill32.png'), "Add Drill") @@ -844,18 +870,29 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # self.splitter.sizes()[0] is actually the size of the "notebook" self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0]) self.final_save.emit() - QtWidgets.qApp.quit() + + if self.app.should_we_quit is True: + QtWidgets.qApp.quit() + else: + self.app.should_we_quit = True + event.ignore() class GeneralPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent=parent) - self.layout = QtWidgets.QVBoxLayout() + self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) - self.general_group = GeneralPrefGroupUI() - self.general_group.setFixedWidth(260) - self.layout.addWidget(self.general_group) + self.general_app_group = GeneralAppPrefGroupUI() + self.general_app_group.setFixedWidth(260) + + self.general_gui_group = GeneralGUIPrefGroupUI() + self.general_gui_group.setFixedWidth(260) + + self.layout.addWidget(self.general_app_group) + self.layout.addWidget(self.general_gui_group) + self.layout.addStretch() class GerberPreferencesUI(QtWidgets.QWidget): @@ -922,59 +959,15 @@ class OptionsGroupUI(QtWidgets.QGroupBox): self.setLayout(self.layout) -class GeneralPrefGroupUI(OptionsGroupUI): +class GeneralGUIPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): - super(GeneralPrefGroupUI, self).__init__(self) + super(GeneralGUIPrefGroupUI, self).__init__(self) - self.setTitle(str("Global Preferences")) + self.setTitle(str("GUI Preferences")) # Create a form layout for the Application general settings self.form_box = QtWidgets.QFormLayout() - # Units for FlatCAM - self.unitslabel = QtWidgets.QLabel('Units:') - self.unitslabel.setToolTip("Those are units in which FlatCAM works.") - self.units_radio = RadioSet([{'label': 'IN', 'value': 'IN'}, - {'label': 'MM', 'value': 'MM'}]) - - # Shell StartUp CB - self.shell_startup_label = QtWidgets.QLabel('Shell at StartUp:') - self.shell_startup_label.setToolTip( - "Check this box if you want the shell to\n" - "start automatically at startup." - ) - self.shell_startup_cb = FCCheckBox(label='') - self.shell_startup_cb.setToolTip( - "Check this box if you want the shell to\n" - "start automatically at startup." - ) - - # Version Check CB - self.version_check_label = QtWidgets.QLabel('Version Check:') - self.version_check_label.setToolTip( - "Check this box if you want to check\n" - "for a new version automatically at startup." - ) - self.version_check_cb = FCCheckBox(label='') - self.version_check_cb.setToolTip( - "Check this box if you want to check\n" - "for a new version automatically at startup." - ) - - # Send Stats CB - self.send_stats_label = QtWidgets.QLabel('Send Stats:') - self.send_stats_label.setToolTip( - "Check this box if you agree to send anonymous\n" - "stats automatically at startup, to help improve FlatCAM." - ) - self.send_stats_cb= FCCheckBox(label='') - self.send_stats_cb.setToolTip( - "Check this box if you agree to send anonymous\n" - "stats automatically at startup, to help improve FlatCAM." - ) - - self.ois_version_check = OptionalInputSection(self.version_check_cb, [self.send_stats_cb]) - # Grid X Entry self.gridx_label = QtWidgets.QLabel('Grid X value:') self.gridx_label.setToolTip( @@ -989,30 +982,6 @@ class GeneralPrefGroupUI(OptionsGroupUI): ) self.gridy_entry = LengthEntry() - # Select mouse pan button - self.panbuttonlabel = QtWidgets.QLabel('Pan Button:') - self.panbuttonlabel.setToolTip("Select the mouse button to use for panning.") - self.pan_button_radio = RadioSet([{'label': 'Middle But.', 'value': '3'}, - {'label': 'Right But.', 'value': '2'}]) - - # Multiple Selection Modifier Key - self.mselectlabel = QtWidgets.QLabel('Multiple Sel:') - self.mselectlabel.setToolTip("Select the key used for multiple selection.") - self.mselect_radio = RadioSet([{'label': 'CTRL', 'value': 'Control'}, - {'label': 'SHIFT', 'value': 'Shift'}]) - - # # Mouse panning with "Space" key, CB - # self.pan_with_space_label = QtWidgets.QLabel('Pan w/ Space:') - # self.pan_with_space_label.setToolTip( - # "Check this box if you want to pan when mouse is moved,\n" - # "and key 'Space' is pressed." - # ) - # self.pan_with_space_cb = FCCheckBox(label='') - # self.pan_with_space_cb.setToolTip( - # "Check this box if you want to pan when mouse is moved,\n" - # "and key 'Space' is pressed." - # ) - # Workspace self.workspace_lbl = QtWidgets.QLabel('Workspace:') self.workspace_lbl.setToolTip( @@ -1214,17 +1183,12 @@ class GeneralPrefGroupUI(OptionsGroupUI): self.spacelabel = QtWidgets.QLabel('') # Add (label - input field) pair to the QFormLayout - self.form_box.addRow(self.unitslabel, self.units_radio) + self.form_box.addRow(self.spacelabel, self.spacelabel) - self.form_box.addRow(self.shell_startup_label, self.shell_startup_cb) - self.form_box.addRow(self.version_check_label, self.version_check_cb) - self.form_box.addRow(self.send_stats_label, self.send_stats_cb) self.form_box.addRow(self.gridx_label, self.gridx_entry) self.form_box.addRow(self.gridy_label, self.gridy_entry) - self.form_box.addRow(self.panbuttonlabel, self.pan_button_radio) - self.form_box.addRow(self.mselectlabel, self.mselect_radio) - # self.form_box.addRow(self.pan_with_space_label, self.pan_with_space_cb) + self.form_box.addRow(self.workspace_lbl, self.workspace_cb) self.form_box.addRow(self.workspace_type_lbl, self.wk_cb) self.form_box.addRow(self.spacelabel, self.spacelabel) @@ -1245,6 +1209,114 @@ class GeneralPrefGroupUI(OptionsGroupUI): self.layout.addLayout(self.form_box) +class GeneralAppPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + super(GeneralAppPrefGroupUI, self).__init__(self) + + self.setTitle(str("App Preferences")) + + # Create a form layout for the Application general settings + self.form_box = QtWidgets.QFormLayout() + + # Units for FlatCAM + self.unitslabel = QtWidgets.QLabel('Units:') + self.unitslabel.setToolTip("Those are units in which FlatCAM works.") + self.units_radio = RadioSet([{'label': 'IN', 'value': 'IN'}, + {'label': 'MM', 'value': 'MM'}]) + + # Languages for FlatCAM + self.languagelabel = QtWidgets.QLabel('Languages:') + self.languagelabel.setToolTip("Set the language used for FlatCAM texts.") + self.language_cb = FCComboBox() + self.languagespace = QtWidgets.QLabel('') + self.language_apply_btn = FCButton("Apply Language") + + # Shell StartUp CB + self.shell_startup_label = QtWidgets.QLabel('Shell at StartUp:') + self.shell_startup_label.setToolTip( + "Check this box if you want the shell to\n" + "start automatically at startup." + ) + self.shell_startup_cb = FCCheckBox(label='') + self.shell_startup_cb.setToolTip( + "Check this box if you want the shell to\n" + "start automatically at startup." + ) + + # Version Check CB + self.version_check_label = QtWidgets.QLabel('Version Check:') + self.version_check_label.setToolTip( + "Check this box if you want to check\n" + "for a new version automatically at startup." + ) + self.version_check_cb = FCCheckBox(label='') + self.version_check_cb.setToolTip( + "Check this box if you want to check\n" + "for a new version automatically at startup." + ) + + # Send Stats CB + self.send_stats_label = QtWidgets.QLabel('Send Stats:') + self.send_stats_label.setToolTip( + "Check this box if you agree to send anonymous\n" + "stats automatically at startup, to help improve FlatCAM." + ) + self.send_stats_cb= FCCheckBox(label='') + self.send_stats_cb.setToolTip( + "Check this box if you agree to send anonymous\n" + "stats automatically at startup, to help improve FlatCAM." + ) + + self.ois_version_check = OptionalInputSection(self.version_check_cb, [self.send_stats_cb]) + + # Select mouse pan button + self.panbuttonlabel = QtWidgets.QLabel('Pan Button:') + self.panbuttonlabel.setToolTip("Select the mouse button to use for panning.") + self.pan_button_radio = RadioSet([{'label': 'Middle But.', 'value': '3'}, + {'label': 'Right But.', 'value': '2'}]) + + # Multiple Selection Modifier Key + self.mselectlabel = QtWidgets.QLabel('Multiple Sel:') + self.mselectlabel.setToolTip("Select the key used for multiple selection.") + self.mselect_radio = RadioSet([{'label': 'CTRL', 'value': 'Control'}, + {'label': 'SHIFT', 'value': 'Shift'}]) + + # # Mouse panning with "Space" key, CB + # self.pan_with_space_label = QtWidgets.QLabel('Pan w/ Space:') + # self.pan_with_space_label.setToolTip( + # "Check this box if you want to pan when mouse is moved,\n" + # "and key 'Space' is pressed." + # ) + # self.pan_with_space_cb = FCCheckBox(label='') + # self.pan_with_space_cb.setToolTip( + # "Check this box if you want to pan when mouse is moved,\n" + # "and key 'Space' is pressed." + # ) + + + # Just to add empty rows + self.spacelabel = QtWidgets.QLabel('') + + # Add (label - input field) pair to the QFormLayout + self.form_box.addRow(self.unitslabel, self.units_radio) + self.form_box.addRow(self.languagelabel, self.language_cb) + self.form_box.addRow(self.languagespace, self.language_apply_btn) + + self.form_box.addRow(self.spacelabel, self.spacelabel) + self.form_box.addRow(self.shell_startup_label, self.shell_startup_cb) + self.form_box.addRow(self.version_check_label, self.version_check_cb) + self.form_box.addRow(self.send_stats_label, self.send_stats_cb) + + self.form_box.addRow(self.panbuttonlabel, self.pan_button_radio) + self.form_box.addRow(self.mselectlabel, self.mselect_radio) + # self.form_box.addRow(self.pan_with_space_label, self.pan_with_space_cb) + self.form_box.addRow(self.spacelabel, self.spacelabel) + + # Add the QFormLayout that holds the Application general defaults + # to the main layout of this TAB + self.layout.addLayout(self.form_box) + + class GerberPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): # OptionsGroupUI.__init__(self, "Gerber Options", parent=parent) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index a82ee97e..62bcd6c5 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1295,7 +1295,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): """ excellon_code = '' - units = self.app.general_options_form.general_group.units_radio.get_value().upper() + units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() # store here if the file has slots, return 1 if any slots, 0 if only drills has_slots = 0 @@ -1355,7 +1355,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): """ excellon_code = '' - units = self.app.general_options_form.general_group.units_radio.get_value().upper() + units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() # store here if the file has slots, return 1 if any slots, 0 if only drills has_slots = 0 @@ -2862,7 +2862,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.ui.geo_tools_table.setCurrentItem(self.ui.geo_tools_table.item(row, 0)) def export_dxf(self): - units = self.app.general_options_form.general_group.units_radio.get_value().upper() + units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() dwg = None try: dwg = ezdxf.new('R2010') diff --git a/FlatCAMTool.py b/FlatCAMTool.py index bbc67812..585f9fa1 100644 --- a/FlatCAMTool.py +++ b/FlatCAMTool.py @@ -33,7 +33,7 @@ class FlatCAMTool(QtWidgets.QWidget): self.menuAction = None - def install(self, icon=None, separator=None, **kwargs): + def install(self, icon=None, separator=None, shortcut=None, **kwargs): before = None # 'pos' is the menu where the Action has to be installed @@ -54,8 +54,13 @@ class FlatCAMTool(QtWidgets.QWidget): # if provided, add an icon to this Action if icon is not None: self.menuAction.setIcon(icon) + # set the text name of the Action, which will be displayed in the menu - self.menuAction.setText(self.toolName) + if shortcut is None: + self.menuAction.setText(self.toolName) + else: + self.menuAction.setText(self.toolName + '\t%s' % shortcut) + # add a ToolTip to the new Action # self.menuAction.setToolTip(self.toolTip) # currently not available diff --git a/ObjectCollection.py b/ObjectCollection.py index 6e7190a1..359bf880 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -262,6 +262,9 @@ class ObjectCollection(QtCore.QAbstractItemModel): if key == QtCore.Qt.Key_G: self.app.on_fileopengerber() + if key == QtCore.Qt.Key_N: + self.app.on_file_new_click() + if key == QtCore.Qt.Key_M: self.app.measurement_tool.run() if key == QtCore.Qt.Key_O: @@ -272,6 +275,11 @@ class ObjectCollection(QtCore.QAbstractItemModel): return elif modifiers == QtCore.Qt.ShiftModifier: + # Copy Object Name + # Copy Object Name + if key == QtCore.Qt.Key_C: + self.app.on_copy_name() + # Toggle axis if key == QtCore.Qt.Key_G: if self.toggle_axis is False: @@ -286,11 +294,51 @@ class ObjectCollection(QtCore.QAbstractItemModel): self.appplotcanvas.redraw() self.app.toggle_axis = False + # Open Preferences Window + if key == QtCore.Qt.Key_P: + self.app.on_preferences() + return + # Rotate Object by 90 degree CCW if key == QtCore.Qt.Key_R: self.app.on_rotate(silent=True, preset=-90) return + # Toggle Workspace + if key == QtCore.Qt.Key_W: + self.app.on_workspace_menu() + return + + # Skew on X axis + if key == QtCore.Qt.Key_X: + self.app.on_skewx() + return + + # Skew on Y axis + if key == QtCore.Qt.Key_Y: + self.app.on_skewy() + return + elif modifiers == QtCore.Qt.AltModifier: + # 2-Sided PCB Tool + if key == QtCore.Qt.Key_D: + self.app.dblsidedtool.run() + return + + # Non-Copper Clear Tool + if key == QtCore.Qt.Key_N: + self.app.ncclear_tool.run() + return + + # Transformation Tool + if key == QtCore.Qt.Key_R: + self.app.measurement_tool.run() + return + + # Cutout Tool + if key == QtCore.Qt.Key_U: + self.app.cutout_tool.run() + return + else: # Zoom Fit if key == QtCore.Qt.Key_1: @@ -316,10 +364,6 @@ class ObjectCollection(QtCore.QAbstractItemModel): select.ui.plot_cb.toggle() self.app.delete_selection_shape() - # Copy Object Name - if key == QtCore.Qt.Key_C: - self.app.on_copy_name() - # Copy Object Name if key == QtCore.Qt.Key_E: self.app.object2editor() @@ -340,12 +384,22 @@ class ObjectCollection(QtCore.QAbstractItemModel): if key == QtCore.Qt.Key_N: self.app.on_new_geometry() + # Set Origin + if key == QtCore.Qt.Key_O: + self.app.on_set_origin() + return + + # Set Origin + if key == QtCore.Qt.Key_P: + self.app.properties_tool.run() + return + # Change Units if key == QtCore.Qt.Key_Q: if self.app.options["units"] == 'MM': - self.app.general_options_form.general_group.units_radio.set_value("IN") + self.app.general_options_form.general_app_group.units_radio.set_value("IN") else: - self.app.general_options_form.general_group.units_radio.set_value("MM") + self.app.general_options_form.general_app_group.units_radio.set_value("MM") self.app.on_toggle_units() # Rotate Object by 90 degree CW diff --git a/PlotCanvas.py b/PlotCanvas.py index ee7ebb7d..56333568 100644 --- a/PlotCanvas.py +++ b/PlotCanvas.py @@ -65,7 +65,7 @@ class PlotCanvas(QtCore.QObject): self.draw_workspace() # if self.app.defaults['global_workspace'] is True: - # if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM': + # if self.app.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM': # self.wkspace_t = Line(pos=) self.shape_collections = [] @@ -92,7 +92,7 @@ class PlotCanvas(QtCore.QObject): a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)]) a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)]) - if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM': + if self.app.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM': if self.app.defaults['global_workspaceT'] == 'A4P': a = a4p_mm elif self.app.defaults['global_workspaceT'] == 'A4L': diff --git a/README.md b/README.md index 1e9d5413..9b2e2bb4 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ CAD program, and create G-Code for Isolation routing. ================================================= +27.01.2018 + +- added more key shortcuts into the application; they are now displayed in the GUI menu's +- reorganized the Edit -> Preferences -> Global +- redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app + + 26.01.2019 - fixed grbl_11 postprocessor in linear_code() function diff --git a/flatcamTools/ToolCalculators.py b/flatcamTools/ToolCalculators.py index d02d765f..dc499972 100644 --- a/flatcamTools/ToolCalculators.py +++ b/flatcamTools/ToolCalculators.py @@ -141,6 +141,9 @@ class ToolCalculator(FlatCAMTool): FlatCAMTool.run(self) self.app.ui.notebook.setTabText(2, "Calc. Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+C', **kwargs) + def on_calculate_tool_dia(self): # Calculation: # Manufacturer gives total angle of the the tip but we need only half of it diff --git a/flatcamTools/ToolCutout.py b/flatcamTools/ToolCutout.py index d11f6044..c7825b62 100644 --- a/flatcamTools/ToolCutout.py +++ b/flatcamTools/ToolCutout.py @@ -9,7 +9,7 @@ from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber class ToolCutout(FlatCAMTool): - toolName = "Cutout PCB Tool" + toolName = "Cutout PCB" def __init__(self, app): FlatCAMTool.__init__(self, app) @@ -188,6 +188,9 @@ class ToolCutout(FlatCAMTool): FlatCAMTool.run(self) self.app.ui.notebook.setTabText(2, "Cutout Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+U', **kwargs) + def on_freeform_cutout(self): def subtract_rectangle(obj_, x0, y0, x1, y1): diff --git a/flatcamTools/ToolDblSided.py b/flatcamTools/ToolDblSided.py index fc2bc5ec..783b14a1 100644 --- a/flatcamTools/ToolDblSided.py +++ b/flatcamTools/ToolDblSided.py @@ -8,7 +8,7 @@ from PyQt5 import QtCore class DblSidedTool(FlatCAMTool): - toolName = "Double-Sided PCB Tool" + toolName = "2-Sided PCB" def __init__(self, app): FlatCAMTool.__init__(self, app) @@ -254,6 +254,9 @@ class DblSidedTool(FlatCAMTool): self.axis_location.set_value('point') self.drill_dia.set_value(1) + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+D', **kwargs) + def on_combo_box_type(self): obj_type = self.box_combo_type.currentIndex() self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) diff --git a/flatcamTools/ToolFilm.py b/flatcamTools/ToolFilm.py index 8d8060c6..94638ada 100644 --- a/flatcamTools/ToolFilm.py +++ b/flatcamTools/ToolFilm.py @@ -6,7 +6,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets class Film(FlatCAMTool): - toolName = "Film PCB Tool" + toolName = "Film PCB" def __init__(self, app): FlatCAMTool.__init__(self, app) @@ -154,6 +154,9 @@ class Film(FlatCAMTool): FlatCAMTool.run(self) self.app.ui.notebook.setTabText(2, "Film Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+L', **kwargs) + def on_film_creation(self): try: name = self.tf_object_combo.currentText() diff --git a/flatcamTools/ToolMeasurement.py b/flatcamTools/ToolMeasurement.py index d7ed0637..b786ad88 100644 --- a/flatcamTools/ToolMeasurement.py +++ b/flatcamTools/ToolMeasurement.py @@ -7,12 +7,12 @@ from math import sqrt class Measurement(FlatCAMTool): - toolName = "Measurement Tool" + toolName = "Measurement" def __init__(self, app): FlatCAMTool.__init__(self, app) - self.units = self.app.general_options_form.general_group.units_radio.get_value().lower() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower() ## Title title_label = QtWidgets.QLabel("%s
" % self.toolName) @@ -165,10 +165,13 @@ class Measurement(FlatCAMTool): # Switch notebook to tool page self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) - self.units = self.app.general_options_form.general_group.units_radio.get_value().lower() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower() self.show() self.app.ui.notebook.setTabText(2, "Meas. Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs) + def on_key_release_meas(self, event): if event.key == 'escape': # abort the measurement action @@ -217,7 +220,7 @@ class Measurement(FlatCAMTool): else: # ENABLE the Measuring TOOL self.active = True - self.units = self.app.general_options_form.general_group.units_radio.get_value().lower() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower() # we disconnect the mouse/key handlers from wherever the measurement tool was called if self.app.call_source == 'app': diff --git a/flatcamTools/ToolMove.py b/flatcamTools/ToolMove.py index 84dc7b19..b7fd1e2e 100644 --- a/flatcamTools/ToolMove.py +++ b/flatcamTools/ToolMove.py @@ -31,7 +31,7 @@ class ToolMove(FlatCAMTool): self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, **kwargs) + FlatCAMTool.install(self, icon, separator, shortcut='M', **kwargs) def run(self): if self.app.tool_tab_locked is True: diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 40ff011d..4782a7bd 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -7,7 +7,7 @@ import time class NonCopperClear(FlatCAMTool, Gerber): - toolName = "Non-Copper Clearing Tool" + toolName = "Non-Copper Clearing" def __init__(self, app): self.app = app @@ -234,7 +234,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.generate_ncc_button.clicked.connect(self.on_ncc) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, **kwargs) + FlatCAMTool.install(self, icon, separator, shortcut='ALT+N', **kwargs) def run(self): FlatCAMTool.run(self) @@ -322,13 +322,13 @@ class NonCopperClear(FlatCAMTool, Gerber): self.obj_name = "" self.ncc_obj = None self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"] - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() def build_ui(self): self.ui_disconnect() # updated units - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() if self.units == "IN": self.addtool_entry.set_value(0.039) diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 16928970..2a14645f 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -5,7 +5,7 @@ from ObjectCollection import * class ToolPaint(FlatCAMTool, Gerber): - toolName = "Paint Area Tool" + toolName = "Paint Area" def __init__(self, app): self.app = app @@ -290,7 +290,7 @@ class ToolPaint(FlatCAMTool, Gerber): def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, **kwargs) + FlatCAMTool.install(self, icon, separator, shortcut='ALT+P', **kwargs) def run(self): FlatCAMTool.run(self) @@ -327,7 +327,7 @@ class ToolPaint(FlatCAMTool, Gerber): self.paintoverlap_entry.set_value(self.default_data["paintoverlap"]) # updated units - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() if self.units == "IN": self.addtool_entry.set_value(0.039) @@ -390,7 +390,7 @@ class ToolPaint(FlatCAMTool, Gerber): pass # updated units - self.units = self.app.general_options_form.general_group.units_radio.get_value().upper() + self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() sorted_tools = [] for k, v in self.paint_tools.items(): diff --git a/flatcamTools/ToolPanelize.py b/flatcamTools/ToolPanelize.py index 905f9293..cdad3943 100644 --- a/flatcamTools/ToolPanelize.py +++ b/flatcamTools/ToolPanelize.py @@ -6,7 +6,7 @@ import time class Panelize(FlatCAMTool): - toolName = "Panelize PCB Tool" + toolName = "Panelize PCB" def __init__(self, app): super(Panelize, self).__init__(self) @@ -197,6 +197,9 @@ class Panelize(FlatCAMTool): FlatCAMTool.run(self) self.app.ui.notebook.setTabText(2, "Panel. Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+Z', **kwargs) + def on_panelize(self): name = self.object_combo.currentText() diff --git a/flatcamTools/ToolProperties.py b/flatcamTools/ToolProperties.py index b2f2eb52..06222f8a 100644 --- a/flatcamTools/ToolProperties.py +++ b/flatcamTools/ToolProperties.py @@ -53,6 +53,9 @@ class Properties(FlatCAMTool): FlatCAMTool.run(self) self.properties() + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='P', **kwargs) + def properties(self): obj_list = self.app.collection.get_selected() if not obj_list: @@ -86,10 +89,10 @@ class Properties(FlatCAMTool): width = abs(ymax - ymin) self.addChild(dims, ['Length:', '%.4f %s' % ( - length, self.app.general_options_form.general_group.units_radio.get_value().lower())], True) + length, self.app.general_options_form.general_app_group.units_radio.get_value().lower())], True) self.addChild(dims, ['Width:', '%.4f %s' % ( - width, self.app.general_options_form.general_group.units_radio.get_value().lower())], True) - if self.app.general_options_form.general_group.units_radio.get_value().lower() == 'mm': + width, self.app.general_options_form.general_app_group.units_radio.get_value().lower())], True) + if self.app.general_options_form.general_app_group.units_radio.get_value().lower() == 'mm': area = (length * width) / 100 self.addChild(dims, ['Box Area:', '%.4f %s' % (area, 'cm2')], True) else: diff --git a/flatcamTools/ToolTransform.py b/flatcamTools/ToolTransform.py index 45b603bc..7d641494 100644 --- a/flatcamTools/ToolTransform.py +++ b/flatcamTools/ToolTransform.py @@ -370,6 +370,9 @@ class ToolTransform(FlatCAMTool): FlatCAMTool.run(self) self.app.ui.notebook.setTabText(2, "Transform Tool") + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs) + def on_rotate(self): try: value = float(self.rotate_entry.get_value()) diff --git a/share/toggle_units16.png b/share/toggle_units16.png new file mode 100644 index 0000000000000000000000000000000000000000..164b823f66c1461db46fb1705d6d20a800720833 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!AqVljv*HQb0_WQV|0{o6YpHH)k)oPVQ}CM zla^bDE4W0@s$J2V9yUej{Dagv`Pb&>sBN--t)hNL<@x=&idqib;wP^^(%AP-;8@xv5>FE{T2p4L4_JKZ}CZ@;7FhTO6{|yEx;4pmyHVg%SQ& z8rsZkiXMx$)`4j`4@AxPE=+<&Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0h>ugK~z{r#g@G; z1W_1<2L&CSzi1Q^YLzQ+3I47C3C$`sTmTYE4LUVil+=DMKq;XhB>tZF%zT^4?q>I_ zIcN4so}4+`yze(T&PXECovj_Ag3mbZb|tfcUwDl#SZWCiU%(zb!O}`7{1-1#;i9w> zHu)C5;t_VCh+abBKe*_2Niu^g>0^~>C2aB!+(nzZ5y4C358N#JmN>@3CG5bRx?PHI zi4lCqL$~_}Hu*ueZla+Qnr_nV>KVj4yup6`&@TDcF6MA8eXKH&t+fncknnG;J>{(k z7Ou&cQ6;qc5F}g&Zr|-vd`kqIT&G>@JQ@mr!%?@ZC+KSzI_>M}W0kRNttHrN|3v+1 zwS&U%vGC+exP!V}d`kog*W}x7m)>Z)*?dX_oBRs(N!3?PSP9*5%`TZj;rbuW4xBz1 z`kt?%p%PQ+V;#d-C|o?^E%v&dKS9F91U}&z`j!Y5Zdi$PY*iw7xREc>rym*+E!=2U zB6_%CB`(lPDEtAVha344<5+6AVI}lUC_UUzi9PH{cw~~~4?n_#r4gPU@Bjb+07*qo IM6N<$f}O?S3IG5A literal 0 HcmV?d00001 From 4f7b2bbb34f694d485f2a19bd844da611ca861a5 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 05:03:23 +0200 Subject: [PATCH 06/23] - added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) --- FlatCAMApp.py | 8 +++++++ FlatCAMGUI.py | 22 ++++++++++++++++++ FlatCAMObj.py | 15 +++++++++++- README.md | 2 +- camlib.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 37e2c233..2542c50d 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -363,6 +363,8 @@ class App(QtCore.QObject): "excellon_optimization_type": self.excellon_defaults_form.excellon_group.excellon_optimization_radio, "excellon_gcode_type": self.excellon_defaults_form.excellon_group.excellon_gcode_type_radio, "geometry_plot": self.geometry_defaults_form.geometry_group.plot_cb, + "geometry_segx": self.geometry_defaults_form.geometry_group.segx_entry, + "geometry_segy": self.geometry_defaults_form.geometry_group.segy_entry, "geometry_cutz": self.geometry_defaults_form.geometry_group.cutz_entry, "geometry_travelz": self.geometry_defaults_form.geometry_group.travelz_entry, "geometry_feedrate": self.geometry_defaults_form.geometry_group.cncfeedrate_entry, @@ -486,6 +488,8 @@ class App(QtCore.QObject): "excellon_gcode_type": "drills", "geometry_plot": True, + "geometry_segx": 0.0, + "geometry_segy": 0.0, "geometry_cutz": -0.002, "geometry_travelz": 0.1, "geometry_toolchange": False, @@ -628,6 +632,8 @@ class App(QtCore.QObject): "excellon_units": self.excellon_options_form.excellon_group.excellon_units_radio, "excellon_optimization_type": self.excellon_options_form.excellon_group.excellon_optimization_radio, "geometry_plot": self.geometry_options_form.geometry_group.plot_cb, + "geometry_segx": self.geometry_options_form.geometry_group.segx_entry, + "geometry_segy": self.geometry_options_form.geometry_group.segy_entry, "geometry_cutz": self.geometry_options_form.geometry_group.cutz_entry, "geometry_travelz": self.geometry_options_form.geometry_group.travelz_entry, "geometry_feedrate": self.geometry_options_form.geometry_group.cncfeedrate_entry, @@ -709,6 +715,8 @@ class App(QtCore.QObject): "excellon_endz": 2.0, "excellon_zeros": "L", "geometry_plot": True, + "geometry_segx": 0.0, + "geometry_segy": 0.0, "geometry_cutz": -0.002, "geometry_travelz": 0.1, "geometry_feedrate": 3.0, diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index d0b19797..40263d8e 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -2260,6 +2260,28 @@ class GeometryPrefGroupUI(OptionsGroupUI): self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus) grid2.addWidget(self.pp_geometry_name_cb, 16, 1) + # Size of trace segment on X axis + segx_label = QtWidgets.QLabel("Seg. X size:") + segx_label.setToolTip( + "The size of the trace segment on the X axis.\n" + "Useful for auto-leveling.\n" + "A value of 0 means no segmentation on the X axis." + ) + grid2.addWidget(segx_label, 17, 0) + self.segx_entry = FCEntry() + grid2.addWidget(self.segx_entry, 17, 1) + + # Size of trace segment on Y axis + segy_label = QtWidgets.QLabel("Seg. Y size:") + segy_label.setToolTip( + "The size of the trace segment on the Y axis.\n" + "Useful for auto-leveling.\n" + "A value of 0 means no segmentation on the Y axis." + ) + grid2.addWidget(segy_label, 18, 0) + self.segy_entry = FCEntry() + grid2.addWidget(self.segy_entry, 18, 1) + # ------------------------------ ## Paint area # ------------------------------ diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 62bcd6c5..761f03a8 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -2962,7 +2962,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): else: self.app.inform.emit("[error_notcl] Failed. No tool selected in the tool table ...") - def mtool_gen_cncjob(self, use_thread=True): + def mtool_gen_cncjob(self, segx=None, segy=None, use_thread=True): """ Creates a multi-tool CNCJob out of this Geometry object. The actual work is done by the target FlatCAMCNCjob object's @@ -2986,6 +2986,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia outname = "%s_%s" % (self.options["name"], 'cnc') + 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']) + # Object initialization function for app.new_object() # RUNNING ON SEPARATE THREAD! def job_init_single_geometry(job_obj, app_obj): @@ -3004,6 +3007,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # job_obj.create_geometry() job_obj.options['Tools_in_use'] = self.get_selected_tools_table_items() + job_obj.segx = segx + job_obj.segy = segy for tooluid_key in self.sel_tools: tool_cnt += 1 @@ -3345,6 +3350,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): toolchange=None, toolchangez=None, toolchangexy=None, extracut=None, startz=None, endz=None, ppname_g=None, + segx=None, + segy=None, use_thread=True): """ Only used for TCL Command. @@ -3376,6 +3383,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): multidepth = multidepth if multidepth is not None else self.options["multidepth"] depthperpass = depthperpass if depthperpass is not None else self.options["depthperpass"] + 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']) + extracut = extracut if extracut is not None else self.options["extracut"] startz = startz if startz is not None else self.options["startz"] endz = endz if endz is not None else self.options["endz"] @@ -3410,6 +3420,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): job_obj.options['type'] = 'Geometry' job_obj.options['tool_dia'] = tooldia + job_obj.segx = segx + job_obj.segy = segy + # TODO: The tolerance should not be hard coded. Just for testing. job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=0.0005, z_cut=z_cut, z_move=z_move, diff --git a/README.md b/README.md index 9b2e2bb4..176bedfc 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ CAD program, and create G-Code for Isolation routing. - added more key shortcuts into the application; they are now displayed in the GUI menu's - reorganized the Edit -> Preferences -> Global - redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app - +- added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) 26.01.2019 diff --git a/camlib.py b/camlib.py index 6756371f..9d65a705 100644 --- a/camlib.py +++ b/camlib.py @@ -4332,6 +4332,8 @@ class CNCjob(Geometry): spindlespeed=None, dwell=True, dwelltime=1000, toolchangez=0.787402, endz=2.0, + segx=None, + segy=None, steps_per_circle=None): # Used when parsing G-code arcs @@ -4376,6 +4378,9 @@ class CNCjob(Geometry): self.dwell = dwell self.dwelltime = dwelltime + self.segx = float(segx) if segx is not None else 0.0 + self.segy = float(segy) if segy is not None else 0.0 + self.input_geometry_bounds = None # Attributes to be included in serialization @@ -5456,6 +5461,61 @@ class CNCjob(Geometry): self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed]) return self.solid_geometry + # code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ + def segment(self, coords): + """ + break long linear lines to make it more auto level friendly + """ + + if len(coords) < 2 or self.segx <= 0 and self.segy <= 0: + return list(coords) + + path = [coords[0]] + + # break the line in either x or y dimension only + def linebreak_single(line, dim, dmax): + if dmax <= 0: + return None + + if line[1][dim] > line[0][dim]: + sign = 1.0 + d = line[1][dim] - line[0][dim] + else: + sign = -1.0 + d = line[0][dim] - line[1][dim] + if d > dmax: + # make sure we don't make any new lines too short + if d > dmax * 2: + dd = dmax + else: + dd = d / 2 + other = dim ^ 1 + return (line[0][dim] + dd * sign, line[0][other] + \ + dd * (line[1][other] - line[0][other]) / d) + return None + + # recursively breaks down a given line until it is within the + # required step size + def linebreak(line): + pt_new = linebreak_single(line, 0, self.segx) + if pt_new is None: + pt_new2 = linebreak_single(line, 1, self.segy) + else: + pt_new2 = linebreak_single((line[0], pt_new), 1, self.segy) + if pt_new2 is not None: + pt_new = pt_new2[::-1] + + if pt_new is None: + path.append(line[1]) + else: + path.append(pt_new) + linebreak((pt_new, line[1])) + + for pt in coords[1:]: + linebreak((path[-1], pt)) + + return path + def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False): @@ -5500,7 +5560,9 @@ class CNCjob(Geometry): gcode = "" - path = list(target_linear.coords) + # path = list(target_linear.coords) + path = self.segment(target_linear.coords) + p = self.pp_geometry # Move fast to 1st point From 18fb7c2c1f08f9aa527390f035f7d0db7fe1359a Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 05:50:41 +0200 Subject: [PATCH 07/23] - added shortcut key 'L' for creating 'New Excellon' --- FlatCAMApp.py | 5 ++++- FlatCAMGUI.py | 2 +- ObjectCollection.py | 4 ++++ README.md | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 2542c50d..8857125b 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -91,7 +91,7 @@ class App(QtCore.QObject): # Version version = 8.905 - version_date = "2019/01/26" + version_date = "2019/01/28" beta = True # URL for update checks and statistics @@ -3716,6 +3716,9 @@ class App(QtCore.QObject): if event.key == 'J': self.on_jump_to() + if event.key == 'L': + self.new_excellon_object() + if event.key == 'M': self.move_tool.toggle() return diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 40263d8e..ae67133b 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -155,7 +155,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ### Edit ### self.menuedit = self.menu.addMenu('&Edit') self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), '&New Geometry\tN') - self.menueditnewexc = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Excellon\tX') + self.menueditnewexc = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Excellon\tL') # Separator self.menuedit.addSeparator() self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Object\tE') diff --git a/ObjectCollection.py b/ObjectCollection.py index 359bf880..43a42625 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -376,6 +376,10 @@ class ObjectCollection(QtCore.QAbstractItemModel): if key == QtCore.Qt.Key_J: self.app.on_jump_to() + # New Excellon + if key == QtCore.Qt.Key_L: + self.app.new_excellon_object() + # Move tool toggle if key == QtCore.Qt.Key_M: self.app.move_tool.toggle() diff --git a/README.md b/README.md index 176bedfc..bf3b6691 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ CAD program, and create G-Code for Isolation routing. - reorganized the Edit -> Preferences -> Global - redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app - added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) +- added shortcut key 'L' for creating 'New Excellon' 26.01.2019 From a9bc0dea5611734f4e31e66bb70d0adf9daffa54 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 06:04:50 +0200 Subject: [PATCH 08/23] - added shortcut key combo 'SHIFT+S' for Running a Script. --- FlatCAMApp.py | 6 ++++++ FlatCAMGUI.py | 2 +- ObjectCollection.py | 7 ++++++- README.md | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 8857125b..82f5a913 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -3672,6 +3672,10 @@ class App(QtCore.QObject): if event.key == 'R': self.on_rotate(silent=True, preset=-90) + # Run a Script + if event.key == 'S': + self.on_filerunscript() + # Toggle Workspace if event.key == 'W': self.on_workspace_menu() @@ -3781,6 +3785,7 @@ class App(QtCore.QObject): E: Edit Object (if selected)
G: Grid On/Off
J: Jump to Coordinates
+L: New Excellon
M: Move Obj
M: Move Geo Item (when in Edit Mode)
N: New Geometry
@@ -3812,6 +3817,7 @@ class App(QtCore.QObject): SHIFT+G: Toggle the axis
SHIFT+P: Open Preferences Window
SHIFT+R: Rotate by 90 degree CCW
+SHIFT+S: Run a Script
SHIFT+W: Toggle the workspace
SHIFT+X: Skew on X axis
SHIFT+Y: Skew on Y axis
diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index ae67133b..91e9cc89 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -72,7 +72,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufile.addSeparator() # Run Scripts - self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), 'Run Script ...', self) + self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), 'Run Script ...\tSHIFT+S', self) self.menufile.addAction(self.menufilerunscript) # Separator diff --git a/ObjectCollection.py b/ObjectCollection.py index 43a42625..2db3610b 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -304,6 +304,11 @@ class ObjectCollection(QtCore.QAbstractItemModel): self.app.on_rotate(silent=True, preset=-90) return + # Run a Script + if key == QtCore.Qt.Key_S: + self.app.on_filerunscript() + return + # Toggle Workspace if key == QtCore.Qt.Key_W: self.app.on_workspace_menu() @@ -331,7 +336,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): # Transformation Tool if key == QtCore.Qt.Key_R: - self.app.measurement_tool.run() + self.app.transform_tool.run() return # Cutout Tool diff --git a/README.md b/README.md index bf3b6691..de19f390 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ CAD program, and create G-Code for Isolation routing. - redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app - added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) - added shortcut key 'L' for creating 'New Excellon' +- added shortcut key combo 'SHIFT+S' for Running a Script. 26.01.2019 From 90ec8b389953fd97e5299cb74f8624b1a4f1bfce Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 14:01:59 +0200 Subject: [PATCH 09/23] - modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M02 (laser active) whenever a value is enter in the Spindlespeed entry field --- ObjectUI.py | 5 +++-- README.md | 1 + postprocessors/grbl_laser.py | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ObjectUI.py b/ObjectUI.py index e8ad7bc9..d16bf157 100644 --- a/ObjectUI.py +++ b/ObjectUI.py @@ -970,8 +970,9 @@ class GeometryObjectUI(ObjectUI): # Spindlespeed spdlabel = QtWidgets.QLabel('Spindle speed:') spdlabel.setToolTip( - "Speed of the spindle\n" - "in RPM (optional)" + "Speed of the spindle in RPM (optional).\n" + "If LASER postprocessor is used,\n" + "this value is the power of laser." ) self.grid3.addWidget(spdlabel, 14, 0) self.cncspindlespeed_entry = IntEntry(allow_empty=True) diff --git a/README.md b/README.md index de19f390..c00eb022 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ CAD program, and create G-Code for Isolation routing. - added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) - added shortcut key 'L' for creating 'New Excellon' - added shortcut key combo 'SHIFT+S' for Running a Script. +- modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M02 (laser active) whenever a value is enter in the Spindlespeed entry field 26.01.2019 diff --git a/postprocessors/grbl_laser.py b/postprocessors/grbl_laser.py index e0344ca6..72611f00 100644 --- a/postprocessors/grbl_laser.py +++ b/postprocessors/grbl_laser.py @@ -36,7 +36,10 @@ class grbl_laser(FlatCAMPostProc): return 'M05' def down_code(self, p): - return 'M03' + if p.spindlespeed: + return 'M03 S%d' % p.spindlespeed + else: + return 'M03' def toolchange_code(self, p): return '' @@ -67,10 +70,7 @@ class grbl_laser(FlatCAMPostProc): return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z)) def spindle_code(self, p): - if p.spindlespeed: - return 'S%d' % p.spindlespeed - else: - return '' + return '' def dwell_code(self, p): return '' From ad69be0456928db0861233f531ab317a79c697ad Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 27 Jan 2019 15:46:54 +0200 Subject: [PATCH 10/23] - remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS --- FlatCAMApp.py | 248 +++++++++++--------- FlatCAMGUI.py | 364 +++++++++++++++++------------ README.md | 2 + flatcamTools/ToolCutout.py | 12 +- flatcamTools/ToolNonCopperClear.py | 38 +-- 5 files changed, 382 insertions(+), 282 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 82f5a913..5fd18e02 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -288,6 +288,7 @@ class App(QtCore.QObject): self.excellon_defaults_form = ExcellonPreferencesUI() self.geometry_defaults_form = GeometryPreferencesUI() self.cncjob_defaults_form = CNCJobPreferencesUI() + self.tools_defaults_form = ToolsPreferencesUI() # when adding entries here read the comments in the method found bellow named: # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True) @@ -318,50 +319,41 @@ class App(QtCore.QObject): "gerber_isotooldia": self.gerber_defaults_form.gerber_group.iso_tool_dia_entry, "gerber_isopasses": self.gerber_defaults_form.gerber_group.iso_width_entry, "gerber_isooverlap": self.gerber_defaults_form.gerber_group.iso_overlap_entry, - "gerber_ncctools": self.gerber_defaults_form.gerber_group.ncc_tool_dia_entry, - "gerber_nccoverlap": self.gerber_defaults_form.gerber_group.ncc_overlap_entry, - "gerber_nccmargin": self.gerber_defaults_form.gerber_group.ncc_margin_entry, - "gerber_nccmethod": self.gerber_defaults_form.gerber_group.ncc_method_radio, - "gerber_nccconnect": self.gerber_defaults_form.gerber_group.ncc_connect_cb, - "gerber_ncccontour": self.gerber_defaults_form.gerber_group.ncc_contour_cb, - "gerber_nccrest": self.gerber_defaults_form.gerber_group.ncc_rest_cb, "gerber_combine_passes": self.gerber_defaults_form.gerber_group.combine_passes_cb, "gerber_milling_type": self.gerber_defaults_form.gerber_group.milling_type_radio, - "gerber_cutouttooldia": self.gerber_defaults_form.gerber_group.cutout_tooldia_entry, - "gerber_cutoutmargin": self.gerber_defaults_form.gerber_group.cutout_margin_entry, - "gerber_cutoutgapsize": self.gerber_defaults_form.gerber_group.cutout_gap_entry, - "gerber_gaps": self.gerber_defaults_form.gerber_group.gaps_radio, "gerber_noncoppermargin": self.gerber_defaults_form.gerber_group.noncopper_margin_entry, "gerber_noncopperrounded": self.gerber_defaults_form.gerber_group.noncopper_rounded_cb, "gerber_bboxmargin": self.gerber_defaults_form.gerber_group.bbmargin_entry, "gerber_bboxrounded": self.gerber_defaults_form.gerber_group.bbrounded_cb, "gerber_circle_steps": self.gerber_defaults_form.gerber_group.circle_steps_entry, - "excellon_plot": self.excellon_defaults_form.excellon_group.plot_cb, - "excellon_solid": self.excellon_defaults_form.excellon_group.solid_cb, - "excellon_drillz": self.excellon_defaults_form.excellon_group.cutz_entry, - "excellon_travelz": self.excellon_defaults_form.excellon_group.travelz_entry, - "excellon_feedrate": self.excellon_defaults_form.excellon_group.feedrate_entry, - "excellon_feedrate_rapid": self.excellon_defaults_form.excellon_group.feedrate_rapid_entry, - "excellon_spindlespeed": self.excellon_defaults_form.excellon_group.spindlespeed_entry, - "excellon_dwell": self.excellon_defaults_form.excellon_group.dwell_cb, - "excellon_dwelltime": self.excellon_defaults_form.excellon_group.dwelltime_entry, - "excellon_toolchange": self.excellon_defaults_form.excellon_group.toolchange_cb, - "excellon_toolchangez": self.excellon_defaults_form.excellon_group.toolchangez_entry, - "excellon_toolchangexy": self.excellon_defaults_form.excellon_group.toolchangexy_entry, - "excellon_ppname_e": self.excellon_defaults_form.excellon_group.pp_excellon_name_cb, - "excellon_startz": self.excellon_defaults_form.excellon_group.estartz_entry, - "excellon_endz": self.excellon_defaults_form.excellon_group.eendz_entry, - "excellon_tooldia": self.excellon_defaults_form.excellon_group.tooldia_entry, - "excellon_slot_tooldia": self.excellon_defaults_form.excellon_group.slot_tooldia_entry, - "excellon_format_upper_in": self.excellon_defaults_form.excellon_group.excellon_format_upper_in_entry, - "excellon_format_lower_in": self.excellon_defaults_form.excellon_group.excellon_format_lower_in_entry, - "excellon_format_upper_mm": self.excellon_defaults_form.excellon_group.excellon_format_upper_mm_entry, - "excellon_format_lower_mm": self.excellon_defaults_form.excellon_group.excellon_format_lower_mm_entry, - "excellon_zeros": self.excellon_defaults_form.excellon_group.excellon_zeros_radio, - "excellon_units": self.excellon_defaults_form.excellon_group.excellon_units_radio, - "excellon_optimization_type": self.excellon_defaults_form.excellon_group.excellon_optimization_radio, - "excellon_gcode_type": self.excellon_defaults_form.excellon_group.excellon_gcode_type_radio, + + "excellon_plot": self.excellon_defaults_form.excellon_gen_group.plot_cb, + "excellon_solid": self.excellon_defaults_form.excellon_gen_group.solid_cb, + "excellon_format_upper_in": self.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry, + "excellon_format_lower_in": self.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry, + "excellon_format_upper_mm": self.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry, + "excellon_format_lower_mm": self.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry, + "excellon_zeros": self.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio, + "excellon_units": self.excellon_defaults_form.excellon_gen_group.excellon_units_radio, + "excellon_optimization_type": self.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio, + + "excellon_drillz": self.excellon_defaults_form.excellon_opt_group.cutz_entry, + "excellon_travelz": self.excellon_defaults_form.excellon_opt_group.travelz_entry, + "excellon_feedrate": self.excellon_defaults_form.excellon_opt_group.feedrate_entry, + "excellon_feedrate_rapid": self.excellon_defaults_form.excellon_opt_group.feedrate_rapid_entry, + "excellon_spindlespeed": self.excellon_defaults_form.excellon_opt_group.spindlespeed_entry, + "excellon_dwell": self.excellon_defaults_form.excellon_opt_group.dwell_cb, + "excellon_dwelltime": self.excellon_defaults_form.excellon_opt_group.dwelltime_entry, + "excellon_toolchange": self.excellon_defaults_form.excellon_opt_group.toolchange_cb, + "excellon_toolchangez": self.excellon_defaults_form.excellon_opt_group.toolchangez_entry, + "excellon_toolchangexy": self.excellon_defaults_form.excellon_opt_group.toolchangexy_entry, + "excellon_ppname_e": self.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb, + "excellon_startz": self.excellon_defaults_form.excellon_opt_group.estartz_entry, + "excellon_endz": self.excellon_defaults_form.excellon_opt_group.eendz_entry, + "excellon_tooldia": self.excellon_defaults_form.excellon_opt_group.tooldia_entry, + "excellon_slot_tooldia": self.excellon_defaults_form.excellon_opt_group.slot_tooldia_entry, + "excellon_gcode_type": self.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio, "geometry_plot": self.geometry_defaults_form.geometry_group.plot_cb, "geometry_segx": self.geometry_defaults_form.geometry_group.segx_entry, "geometry_segy": self.geometry_defaults_form.geometry_group.segy_entry, @@ -397,7 +389,20 @@ class App(QtCore.QObject): "cncjob_fr_decimals": self.cncjob_defaults_form.cncjob_group.fr_dec_entry, "cncjob_prepend": self.cncjob_defaults_form.cncjob_group.prepend_text, "cncjob_append": self.cncjob_defaults_form.cncjob_group.append_text, - "cncjob_steps_per_circle": self.cncjob_defaults_form.cncjob_group.steps_per_circle_entry + "cncjob_steps_per_circle": self.cncjob_defaults_form.cncjob_group.steps_per_circle_entry, + + "tools_ncctools": self.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, + "tools_nccoverlap": self.tools_defaults_form.tools_ncc_group.ncc_overlap_entry, + "tools_nccmargin": self.tools_defaults_form.tools_ncc_group.ncc_margin_entry, + "tools_nccmethod": self.tools_defaults_form.tools_ncc_group.ncc_method_radio, + "tools_nccconnect": self.tools_defaults_form.tools_ncc_group.ncc_connect_cb, + "tools_ncccontour": self.tools_defaults_form.tools_ncc_group.ncc_contour_cb, + "tools_nccrest": self.tools_defaults_form.tools_ncc_group.ncc_rest_cb, + + "tools_cutouttooldia": self.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry, + "tools_cutoutmargin": self.tools_defaults_form.tools_cutout_group.cutout_margin_entry, + "tools_cutoutgapsize": self.tools_defaults_form.tools_cutout_group.cutout_gap_entry, + "tools_gaps_rect": self.tools_defaults_form.tools_cutout_group.gaps_radio } # loads postprocessors self.postprocessors = load_postprocessors(self) @@ -407,7 +412,7 @@ class App(QtCore.QObject): # HPGL postprocessor is only for Geometry objects therefore it should not be in the Excellon Preferences if name == 'hpgl': continue - self.excellon_defaults_form.excellon_group.pp_excellon_name_cb.addItem(name) + self.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb.addItem(name) self.defaults = LoudDict() self.defaults.set_change_callback(self.on_defaults_dict_change) # When the dictionary changes. @@ -440,20 +445,9 @@ class App(QtCore.QObject): "gerber_isotooldia": 0.016, "gerber_isopasses": 1, "gerber_isooverlap": 0.15, - "gerber_ncctools": "1.0, 0.5", - "gerber_nccoverlap": 0.4, - "gerber_nccmargin": 1, - "gerber_nccmethod": "seed", - "gerber_nccconnect": True, - "gerber_ncccontour": True, - "gerber_nccrest": False, "gerber_combine_passes": False, "gerber_milling_type": "cl", - "gerber_cutouttooldia": 0.07, - "gerber_cutoutmargin": 0.1, - "gerber_cutoutgapsize": 0.15, - "gerber_gaps": "4", "gerber_noncoppermargin": 0.0, "gerber_noncopperrounded": False, "gerber_bboxmargin": 0.0, @@ -462,6 +456,15 @@ class App(QtCore.QObject): "excellon_plot": True, "excellon_solid": False, + "excellon_format_upper_in": 2, + "excellon_format_lower_in": 4, + "excellon_format_upper_mm": 3, + "excellon_format_lower_mm": 3, + "excellon_zeros": "L", + "excellon_units": "INCH", + "excellon_optimization_type": 'B', + "excellon_search_time": 3, + "excellon_drillz": -0.1, "excellon_travelz": 0.1, "excellon_feedrate": 3.0, @@ -477,14 +480,6 @@ class App(QtCore.QObject): "excellon_startz": None, "excellon_endz": 2.0, "excellon_ppname_e": 'default', - "excellon_format_upper_in": 2, - "excellon_format_lower_in": 4, - "excellon_format_upper_mm": 3, - "excellon_format_lower_mm": 3, - "excellon_zeros": "L", - "excellon_units": "INCH", - "excellon_optimization_type": 'B', - "excellon_search_time": 3, "excellon_gcode_type": "drills", "geometry_plot": True, @@ -524,6 +519,19 @@ class App(QtCore.QObject): "cncjob_prepend": "", "cncjob_append": "", "cncjob_steps_per_circle": 64, + + "tools_ncctools": "1.0, 0.5", + "tools_nccoverlap": 0.4, + "tools_nccmargin": 1, + "tools_nccmethod": "seed", + "tools_nccconnect": True, + "tools_ncccontour": True, + "tools_nccrest": False, + "tools_cutouttooldia": 0.07, + "tools_cutoutmargin": 0.1, + "tools_cutoutgapsize": 0.15, + "tools_gaps_rect": "4", + "global_background_timeout": 300000, # Default value is 5 minutes "global_verbose_error_level": 0, # Shell verbosity 0 = default # (python trace only for unknown errors), @@ -585,6 +593,7 @@ class App(QtCore.QObject): self.excellon_options_form = ExcellonPreferencesUI() self.geometry_options_form = GeometryPreferencesUI() self.cncjob_options_form = CNCJobPreferencesUI() + self.tools_options_form = ToolsPreferencesUI() self.options_form_fields = { "units": self.general_options_form.general_app_group.units_radio, @@ -596,41 +605,37 @@ class App(QtCore.QObject): "gerber_isotooldia": self.gerber_options_form.gerber_group.iso_tool_dia_entry, "gerber_isopasses": self.gerber_options_form.gerber_group.iso_width_entry, "gerber_isooverlap": self.gerber_options_form.gerber_group.iso_overlap_entry, - "gerber_ncctools": self.gerber_options_form.gerber_group.ncc_tool_dia_entry, - "gerber_nccoverlap": self.gerber_options_form.gerber_group.ncc_overlap_entry, - "gerber_nccmargin": self.gerber_options_form.gerber_group.ncc_margin_entry, "gerber_combine_passes": self.gerber_options_form.gerber_group.combine_passes_cb, - "gerber_cutouttooldia": self.gerber_options_form.gerber_group.cutout_tooldia_entry, - "gerber_cutoutmargin": self.gerber_options_form.gerber_group.cutout_margin_entry, - "gerber_cutoutgapsize": self.gerber_options_form.gerber_group.cutout_gap_entry, - "gerber_gaps": self.gerber_options_form.gerber_group.gaps_radio, "gerber_noncoppermargin": self.gerber_options_form.gerber_group.noncopper_margin_entry, "gerber_noncopperrounded": self.gerber_options_form.gerber_group.noncopper_rounded_cb, "gerber_bboxmargin": self.gerber_options_form.gerber_group.bbmargin_entry, "gerber_bboxrounded": self.gerber_options_form.gerber_group.bbrounded_cb, - "excellon_plot": self.excellon_options_form.excellon_group.plot_cb, - "excellon_solid": self.excellon_options_form.excellon_group.solid_cb, - "excellon_drillz": self.excellon_options_form.excellon_group.cutz_entry, - "excellon_travelz": self.excellon_options_form.excellon_group.travelz_entry, - "excellon_feedrate": self.excellon_options_form.excellon_group.feedrate_entry, - "excellon_feedrate_rapid": self.excellon_options_form.excellon_group.feedrate_rapid_entry, - "excellon_spindlespeed": self.excellon_options_form.excellon_group.spindlespeed_entry, - "excellon_dwell": self.excellon_options_form.excellon_group.dwell_cb, - "excellon_dwelltime": self.excellon_options_form.excellon_group.dwelltime_entry, - "excellon_toolchange": self.excellon_options_form.excellon_group.toolchange_cb, - "excellon_toolchangez": self.excellon_options_form.excellon_group.toolchangez_entry, - "excellon_toolchangexy": self.excellon_options_form.excellon_group.toolchangexy_entry, - "excellon_tooldia": self.excellon_options_form.excellon_group.tooldia_entry, - "excellon_ppname_e": self.excellon_options_form.excellon_group.pp_excellon_name_cb, - "excellon_startz": self.excellon_options_form.excellon_group.estartz_entry, - "excellon_endz": self.excellon_options_form.excellon_group.eendz_entry, - "excellon_format_upper_in": self.excellon_options_form.excellon_group.excellon_format_upper_in_entry, - "excellon_format_lower_in": self.excellon_options_form.excellon_group.excellon_format_lower_in_entry, - "excellon_format_upper_mm": self.excellon_options_form.excellon_group.excellon_format_upper_mm_entry, - "excellon_format_lower_mm": self.excellon_options_form.excellon_group.excellon_format_lower_mm_entry, - "excellon_zeros": self.excellon_options_form.excellon_group.excellon_zeros_radio, - "excellon_units": self.excellon_options_form.excellon_group.excellon_units_radio, - "excellon_optimization_type": self.excellon_options_form.excellon_group.excellon_optimization_radio, + + "excellon_plot": self.excellon_options_form.excellon_gen_group.plot_cb, + "excellon_solid": self.excellon_options_form.excellon_gen_group.solid_cb, + "excellon_format_upper_in": self.excellon_options_form.excellon_gen_group.excellon_format_upper_in_entry, + "excellon_format_lower_in": self.excellon_options_form.excellon_gen_group.excellon_format_lower_in_entry, + "excellon_format_upper_mm": self.excellon_options_form.excellon_gen_group.excellon_format_upper_mm_entry, + "excellon_format_lower_mm": self.excellon_options_form.excellon_gen_group.excellon_format_lower_mm_entry, + "excellon_zeros": self.excellon_options_form.excellon_gen_group.excellon_zeros_radio, + "excellon_units": self.excellon_options_form.excellon_gen_group.excellon_units_radio, + "excellon_optimization_type": self.excellon_options_form.excellon_gen_group.excellon_optimization_radio, + + "excellon_drillz": self.excellon_options_form.excellon_opt_group.cutz_entry, + "excellon_travelz": self.excellon_options_form.excellon_opt_group.travelz_entry, + "excellon_feedrate": self.excellon_options_form.excellon_opt_group.feedrate_entry, + "excellon_feedrate_rapid": self.excellon_options_form.excellon_opt_group.feedrate_rapid_entry, + "excellon_spindlespeed": self.excellon_options_form.excellon_opt_group.spindlespeed_entry, + "excellon_dwell": self.excellon_options_form.excellon_opt_group.dwell_cb, + "excellon_dwelltime": self.excellon_options_form.excellon_opt_group.dwelltime_entry, + "excellon_toolchange": self.excellon_options_form.excellon_opt_group.toolchange_cb, + "excellon_toolchangez": self.excellon_options_form.excellon_opt_group.toolchangez_entry, + "excellon_toolchangexy": self.excellon_options_form.excellon_opt_group.toolchangexy_entry, + "excellon_tooldia": self.excellon_options_form.excellon_opt_group.tooldia_entry, + "excellon_ppname_e": self.excellon_options_form.excellon_opt_group.pp_excellon_name_cb, + "excellon_startz": self.excellon_options_form.excellon_opt_group.estartz_entry, + "excellon_endz": self.excellon_options_form.excellon_opt_group.eendz_entry, + "geometry_plot": self.geometry_options_form.geometry_group.plot_cb, "geometry_segx": self.geometry_options_form.geometry_group.segx_entry, "geometry_segy": self.geometry_options_form.geometry_group.segy_entry, @@ -656,15 +661,25 @@ class App(QtCore.QObject): "geometry_depthperpass": self.geometry_options_form.geometry_group.depthperpass_entry, "geometry_multidepth": self.geometry_options_form.geometry_group.multidepth_cb, "geometry_extracut": self.geometry_options_form.geometry_group.extracut_cb, + "cncjob_plot": self.cncjob_options_form.cncjob_group.plot_cb, "cncjob_tooldia": self.cncjob_options_form.cncjob_group.tooldia_entry, "cncjob_prepend": self.cncjob_options_form.cncjob_group.prepend_text, - "cncjob_append": self.cncjob_options_form.cncjob_group.append_text + "cncjob_append": self.cncjob_options_form.cncjob_group.append_text, + + "tools_ncctools": self.tools_options_form.tools_ncc_group.ncc_tool_dia_entry, + "tools_nccoverlap": self.tools_options_form.tools_ncc_group.ncc_overlap_entry, + "tools_nccmargin": self.tools_options_form.tools_ncc_group.ncc_margin_entry, + + "tools_cutouttooldia": self.tools_options_form.tools_cutout_group.cutout_tooldia_entry, + "tools_cutoutmargin": self.tools_options_form.tools_cutout_group.cutout_margin_entry, + "tools_cutoutgapsize": self.tools_options_form.tools_cutout_group.cutout_gap_entry, + "tools_gaps": self.tools_options_form.tools_cutout_group.gaps_radio, } for name in list(self.postprocessors.keys()): self.geometry_options_form.geometry_group.pp_geometry_name_cb.addItem(name) - self.excellon_options_form.excellon_group.pp_excellon_name_cb.addItem(name) + self.excellon_options_form.excellon_opt_group.pp_excellon_name_cb.addItem(name) self.options = LoudDict() self.options.set_change_callback(self.on_options_dict_change) @@ -678,20 +693,22 @@ class App(QtCore.QObject): "gerber_isotooldia": 0.016, "gerber_isopasses": 1, "gerber_isooverlap": 0.15, - "gerber_ncctools": "1.0, 0.5", - "gerber_nccoverlap": 0.4, - "gerber_nccmargin": 1, "gerber_combine_passes": True, - "gerber_cutouttooldia": 0.07, - "gerber_cutoutmargin": 0.1, - "gerber_cutoutgapsize": 0.15, - "gerber_gaps": "4", "gerber_noncoppermargin": 0.0, "gerber_noncopperrounded": False, "gerber_bboxmargin": 0.0, "gerber_bboxrounded": False, "excellon_plot": True, "excellon_solid": False, + "excellon_format_upper_in": 2, + "excellon_format_lower_in": 4, + "excellon_format_upper_mm": 3, + "excellon_format_lower_mm": 3, + "excellon_units": 'INCH', + "excellon_optimization_type": 'B', + "excellon_search_time": 3, + "excellon_zeros": "L", + "excellon_drillz": -0.1, "excellon_travelz": 0.1, "excellon_feedrate": 3.0, @@ -704,16 +721,9 @@ class App(QtCore.QObject): "excellon_toolchangexy": "0.0, 0.0", "excellon_tooldia": 0.016, "excellon_ppname_e": 'default', - "excellon_format_upper_in": 2, - "excellon_format_lower_in": 4, - "excellon_format_upper_mm": 3, - "excellon_format_lower_mm": 3, - "excellon_units": 'INCH', - "excellon_optimization_type": 'B', - "excellon_search_time": 3, "excellon_startz": None, "excellon_endz": 2.0, - "excellon_zeros": "L", + "geometry_plot": True, "geometry_segx": 0.0, "geometry_segy": 0.0, @@ -739,10 +749,20 @@ class App(QtCore.QObject): "geometry_depthperpass": 0.002, "geometry_multidepth": False, "geometry_extracut": False, + "cncjob_plot": True, "cncjob_tooldia": 0.016, "cncjob_prepend": "", "cncjob_append": "", + + "tools_ncctools": "1.0, 0.5", + "tools_nccoverlap": 0.4, + "tools_nccmargin": 1, + "tools_cutouttooldia": 0.07, + "tools_cutoutmargin": 0.1, + "tools_cutoutgapsize": 0.15, + "tools_gaps": "4", + "global_background_timeout": 300000, # Default value is 5 minutes "global_verbose_error_level": 0, # Shell verbosity: # 0 = default(python trace only for unknown errors), @@ -756,6 +776,7 @@ class App(QtCore.QObject): self.exc_form = None self.geo_form = None self.cnc_form = None + self.tools_form = None self.on_options_combo_change(0) # Will show the initial form ### Define OBJECT COLLECTION ### @@ -1091,11 +1112,11 @@ class App(QtCore.QObject): self.general_defaults_form.general_app_group.shell_startup_cb.clicked.connect(self.on_toggle_shell) # Load the defaults values into the Excellon Format and Excellon Zeros fields - self.excellon_defaults_form.excellon_group.excellon_defaults_button.clicked.connect( + self.excellon_defaults_form.excellon_opt_group.excellon_defaults_button.clicked.connect( self.on_excellon_defaults_button) # Load the defaults values into the Excellon Format and Excellon Zeros fields - self.excellon_options_form.excellon_group.excellon_defaults_button.clicked.connect( + self.excellon_options_form.excellon_opt_group.excellon_defaults_button.clicked.connect( self.on_excellon_options_button) # this is a flag to signal to other tools that the ui tooltab is locked and not accessible @@ -2540,9 +2561,9 @@ class App(QtCore.QObject): return # Options to scale - dimensions = ['gerber_isotooldia', 'gerber_cutoutmargin', 'gerber_cutoutgapsize', - 'gerber_noncoppermargin', 'gerber_bboxmargin','gerber_isooverlap','gerber_nccoverlap', - 'gerber_nccmargin','gerber_cutouttooldia','gerber_cutoutgapsize','gerber_cutoutmargin', + dimensions = ['gerber_isotooldia', 'tools_cutoutmargin', 'tools_cutoutgapsize', + 'gerber_noncoppermargin', 'gerber_bboxmargin','gerber_isooverlap','tools_nccoverlap', + 'tools_nccmargin','tools_cutouttooldia','tools_cutoutgapsize', 'gerber_noncoppermargin','gerber_bboxmargin', 'excellon_drillz', "excellon_toolchangexy", 'excellon_travelz', 'excellon_feedrate', 'excellon_feedrate_rapid', 'excellon_toolchangez', @@ -2675,12 +2696,14 @@ class App(QtCore.QObject): self.exc_form = self.excellon_defaults_form self.geo_form = self.geometry_defaults_form self.cnc_form = self.cncjob_defaults_form + self.tools_form = self.tools_defaults_form elif sel == 1: self.gen_form = self.general_options_form self.ger_form = self.gerber_options_form self.exc_form = self.excellon_options_form self.geo_form = self.geometry_options_form self.cnc_form = self.cncjob_options_form + self.tools_form = self.tools_options_form else: return @@ -2719,6 +2742,13 @@ class App(QtCore.QObject): self.ui.cncjob_scroll_area.setWidget(self.cnc_form) self.cnc_form.show() + try: + self.ui.tools_scroll_area.takeWidget() + except: + self.log.debug("Nothing to remove") + self.ui.tools_scroll_area.setWidget(self.tools_form) + self.tools_form.show() + self.log.debug("Finished GUI form initialization.") # self.options2form() diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 91e9cc89..016ebd9b 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -604,6 +604,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.cncjob_scroll_area = VerticalScrollArea() self.cncjob_tab_lay.addWidget(self.cncjob_scroll_area) + self.tools_tab = QtWidgets.QWidget() + self.pref_tab_area.addTab(self.tools_tab, "TOOLS") + self.tools_tab_lay = QtWidgets.QVBoxLayout() + self.tools_tab_lay.setContentsMargins(2, 2, 2, 2) + self.tools_tab.setLayout(self.tools_tab_lay) + + self.tools_scroll_area = VerticalScrollArea() + self.tools_tab_lay.addWidget(self.tools_scroll_area) + self.pref_tab_bottom_layout = QtWidgets.QHBoxLayout() self.pref_tab_bottom_layout.setAlignment(QtCore.Qt.AlignVCenter) self.pref_tab_layout.addLayout(self.pref_tab_bottom_layout) @@ -911,12 +920,17 @@ class ExcellonPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent=parent) - self.layout = QtWidgets.QVBoxLayout() + self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) - self.excellon_group = ExcellonPrefGroupUI() - self.excellon_group.setFixedWidth(260) - self.layout.addWidget(self.excellon_group) + self.excellon_gen_group = ExcellonGenPrefGroupUI() + self.excellon_gen_group.setFixedWidth(260) + self.excellon_opt_group = ExcellonOptPrefGroupUI() + self.excellon_opt_group.setFixedWidth(260) + + self.layout.addWidget(self.excellon_gen_group) + self.layout.addWidget(self.excellon_opt_group) + self.layout.addStretch() class GeometryPreferencesUI(QtWidgets.QWidget): @@ -931,6 +945,22 @@ class GeometryPreferencesUI(QtWidgets.QWidget): self.layout.addWidget(self.geometry_group) +class ToolsPreferencesUI(QtWidgets.QWidget): + + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self, parent=parent) + self.layout = QtWidgets.QHBoxLayout() + self.setLayout(self.layout) + + self.tools_ncc_group = ToolsNCCPrefGroupUI() + self.tools_ncc_group.setFixedWidth(260) + self.tools_cutout_group = ToolsCutoutPrefGroupUI() + self.tools_cutout_group.setFixedWidth(260) + + self.layout.addWidget(self.tools_ncc_group) + self.layout.addWidget(self.tools_cutout_group) + self.layout.addStretch() + class CNCJobPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): @@ -1345,7 +1375,7 @@ class GerberPrefGroupUI(OptionsGroupUI): grid0.addWidget(self.solid_cb, 0, 1) # Multicolored CB - self.multicolored_cb = FCCheckBox(label='Multicolored') + self.multicolored_cb = FCCheckBox(label='M-Color') self.multicolored_cb.setToolTip( "Draw polygons in different colors." ) @@ -1427,148 +1457,6 @@ class GerberPrefGroupUI(OptionsGroupUI): ) self.layout.addWidget(self.clearcopper_label) - grid5 = QtWidgets.QGridLayout() - self.layout.addLayout(grid5) - ncctdlabel = QtWidgets.QLabel('Tools dia:') - ncctdlabel.setToolTip( - "Diameters of the cutting tools, separated by ','" - ) - grid5.addWidget(ncctdlabel, 0, 0) - self.ncc_tool_dia_entry = FCEntry() - grid5.addWidget(self.ncc_tool_dia_entry, 0, 1) - - nccoverlabel = QtWidgets.QLabel('Overlap:') - nccoverlabel.setToolTip( - "How much (fraction) of the tool width to overlap each tool pass.\n" - "Example:\n" - "A value here of 0.25 means 25% from the tool diameter found above.\n\n" - "Adjust the value starting with lower values\n" - "and increasing it if areas that should be cleared are still \n" - "not cleared.\n" - "Lower values = faster processing, faster execution on PCB.\n" - "Higher values = slow processing and slow execution on CNC\n" - "due of too many paths." - ) - grid5.addWidget(nccoverlabel, 1, 0) - self.ncc_overlap_entry = FloatEntry() - grid5.addWidget(self.ncc_overlap_entry, 1, 1) - - nccmarginlabel = QtWidgets.QLabel('Margin:') - nccmarginlabel.setToolTip( - "Bounding box margin." - ) - grid5.addWidget(nccmarginlabel, 2, 0) - self.ncc_margin_entry = FloatEntry() - grid5.addWidget(self.ncc_margin_entry, 2, 1) - - # Method - methodlabel = QtWidgets.QLabel('Method:') - methodlabel.setToolTip( - "Algorithm for non-copper clearing:
" - "Standard: Fixed step inwards.
" - "Seed-based: Outwards from seed.
" - "Line-based: Parallel lines." - ) - grid5.addWidget(methodlabel, 3, 0) - self.ncc_method_radio = RadioSet([ - {"label": "Standard", "value": "standard"}, - {"label": "Seed-based", "value": "seed"}, - {"label": "Straight lines", "value": "lines"} - ], orientation='vertical', stretch=False) - grid5.addWidget(self.ncc_method_radio, 3, 1) - - # Connect lines - pathconnectlabel = QtWidgets.QLabel("Connect:") - pathconnectlabel.setToolTip( - "Draw lines between resulting\n" - "segments to minimize tool lifts." - ) - grid5.addWidget(pathconnectlabel, 4, 0) - self.ncc_connect_cb = FCCheckBox() - grid5.addWidget(self.ncc_connect_cb, 4, 1) - - contourlabel = QtWidgets.QLabel("Contour:") - contourlabel.setToolTip( - "Cut around the perimeter of the polygon\n" - "to trim rough edges." - ) - grid5.addWidget(contourlabel, 5, 0) - self.ncc_contour_cb = FCCheckBox() - grid5.addWidget(self.ncc_contour_cb, 5, 1) - - restlabel = QtWidgets.QLabel("Rest M.:") - restlabel.setToolTip( - "If checked, use 'rest machining'.\n" - "Basically it will clear copper outside PCB features,\n" - "using the biggest tool and continue with the next tools,\n" - "from bigger to smaller, to clear areas of copper that\n" - "could not be cleared by previous tool.\n" - "If not checked, use the standard algorithm." - ) - grid5.addWidget(restlabel, 6, 0) - self.ncc_rest_cb = FCCheckBox() - grid5.addWidget(self.ncc_rest_cb, 6, 1) - - ## Board cuttout - self.board_cutout_label = QtWidgets.QLabel("Board cutout:") - self.board_cutout_label.setToolTip( - "Create toolpaths to cut around\n" - "the PCB and separate it from\n" - "the original board." - ) - self.layout.addWidget(self.board_cutout_label) - - grid2 = QtWidgets.QGridLayout() - self.layout.addLayout(grid2) - tdclabel = QtWidgets.QLabel('Tool dia:') - tdclabel.setToolTip( - "Diameter of the cutting tool." - ) - grid2.addWidget(tdclabel, 0, 0) - self.cutout_tooldia_entry = LengthEntry() - grid2.addWidget(self.cutout_tooldia_entry, 0, 1) - - marginlabel = QtWidgets.QLabel('Margin:') - marginlabel.setToolTip( - "Distance from objects at which\n" - "to draw the cutout." - ) - grid2.addWidget(marginlabel, 1, 0) - self.cutout_margin_entry = LengthEntry() - grid2.addWidget(self.cutout_margin_entry, 1, 1) - - gaplabel = QtWidgets.QLabel('Gap size:') - gaplabel.setToolTip( - "Size of the gaps in the toolpath\n" - "that will remain to hold the\n" - "board in place." - ) - grid2.addWidget(gaplabel, 2, 0) - self.cutout_gap_entry = LengthEntry() - grid2.addWidget(self.cutout_gap_entry, 2, 1) - - gapslabel = QtWidgets.QLabel('Gaps:') - gapslabel.setToolTip( - "Where to place the gaps, Top/Bottom\n" - "Left/Rigt, or on all 4 sides." - ) - grid2.addWidget(gapslabel, 3, 0) - self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'}, - {'label': '2 (L/R)', 'value': 'lr'}, - {'label': '4', 'value': '4'}]) - grid2.addWidget(self.gaps_radio, 3, 1) - - ## Non-copper regions - self.noncopper_label = QtWidgets.QLabel("Non-copper regions:") - self.noncopper_label.setToolTip( - "Create polygons covering the\n" - "areas without copper on the PCB.\n" - "Equivalent to the inverse of this\n" - "object. Can be used to remove all\n" - "copper from a specified region." - ) - self.layout.addWidget(self.noncopper_label) - grid3 = QtWidgets.QGridLayout() self.layout.addLayout(grid3) @@ -1619,13 +1507,13 @@ class GerberPrefGroupUI(OptionsGroupUI): self.layout.addStretch() -class ExcellonPrefGroupUI(OptionsGroupUI): +class ExcellonGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent) - super(ExcellonPrefGroupUI, self).__init__(self) + super(ExcellonGenPrefGroupUI, self).__init__(self) - self.setTitle(str("Excellon Options")) + self.setTitle(str("Excellon General")) # Plot options self.plot_options_label = QtWidgets.QLabel("Plot Options:") @@ -1741,7 +1629,7 @@ class ExcellonPrefGroupUI(OptionsGroupUI): hlay3 = QtWidgets.QHBoxLayout() self.layout.addLayout(hlay3) - self.excellon_zeros_label = QtWidgets.QLabel('Default Zeros Type:') + self.excellon_zeros_label = QtWidgets.QLabel('Default Zeros:') self.excellon_zeros_label.setAlignment(QtCore.Qt.AlignLeft) self.excellon_zeros_label.setToolTip( "This sets the type of Excellon zeros.\n" @@ -1769,7 +1657,7 @@ class ExcellonPrefGroupUI(OptionsGroupUI): hlay4 = QtWidgets.QHBoxLayout() self.layout.addLayout(hlay4) - self.excellon_units_label = QtWidgets.QLabel('Default Units Type:') + self.excellon_units_label = QtWidgets.QLabel('Default Units:') self.excellon_units_label.setAlignment(QtCore.Qt.AlignLeft) self.excellon_units_label.setToolTip( "This sets the default units of Excellon files.\n" @@ -1842,6 +1730,17 @@ class ExcellonPrefGroupUI(OptionsGroupUI): self.excellon_optimization_label.setDisabled(True) self.excellon_optimization_radio.setDisabled(True) + self.layout.addStretch() + + +class ExcellonOptPrefGroupUI(OptionsGroupUI): + + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent) + super(ExcellonOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("Excellon Options")) + ## Create CNC Job self.cncjob_label = QtWidgets.QLabel('Create CNC Job') self.cncjob_label.setToolTip( @@ -2477,6 +2376,167 @@ class CNCJobPrefGroupUI(OptionsGroupUI): self.layout.addLayout(grid0) +class ToolsNCCPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "NCC Tool Options", parent=parent) + super(ToolsNCCPrefGroupUI, self).__init__(self) + + self.setTitle(str("NCC Tool Options")) + + ## Clear non-copper regions + self.clearcopper_label = QtWidgets.QLabel("Clear non-copper:") + self.clearcopper_label.setToolTip( + "Create a Geometry object with\n" + "toolpaths to cut all non-copper regions." + ) + self.layout.addWidget(self.clearcopper_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + ncctdlabel = QtWidgets.QLabel('Tools dia:') + ncctdlabel.setToolTip( + "Diameters of the cutting tools, separated by ','" + ) + grid0.addWidget(ncctdlabel, 0, 0) + self.ncc_tool_dia_entry = FCEntry() + grid0.addWidget(self.ncc_tool_dia_entry, 0, 1) + + nccoverlabel = QtWidgets.QLabel('Overlap:') + nccoverlabel.setToolTip( + "How much (fraction) of the tool width to overlap each tool pass.\n" + "Example:\n" + "A value here of 0.25 means 25% from the tool diameter found above.\n\n" + "Adjust the value starting with lower values\n" + "and increasing it if areas that should be cleared are still \n" + "not cleared.\n" + "Lower values = faster processing, faster execution on PCB.\n" + "Higher values = slow processing and slow execution on CNC\n" + "due of too many paths." + ) + grid0.addWidget(nccoverlabel, 1, 0) + self.ncc_overlap_entry = FloatEntry() + grid0.addWidget(self.ncc_overlap_entry, 1, 1) + + nccmarginlabel = QtWidgets.QLabel('Margin:') + nccmarginlabel.setToolTip( + "Bounding box margin." + ) + grid0.addWidget(nccmarginlabel, 2, 0) + self.ncc_margin_entry = FloatEntry() + grid0.addWidget(self.ncc_margin_entry, 2, 1) + + # Method + methodlabel = QtWidgets.QLabel('Method:') + methodlabel.setToolTip( + "Algorithm for non-copper clearing:
" + "Standard: Fixed step inwards.
" + "Seed-based: Outwards from seed.
" + "Line-based: Parallel lines." + ) + grid0.addWidget(methodlabel, 3, 0) + self.ncc_method_radio = RadioSet([ + {"label": "Standard", "value": "standard"}, + {"label": "Seed-based", "value": "seed"}, + {"label": "Straight lines", "value": "lines"} + ], orientation='vertical', stretch=False) + grid0.addWidget(self.ncc_method_radio, 3, 1) + + # Connect lines + pathconnectlabel = QtWidgets.QLabel("Connect:") + pathconnectlabel.setToolTip( + "Draw lines between resulting\n" + "segments to minimize tool lifts." + ) + grid0.addWidget(pathconnectlabel, 4, 0) + self.ncc_connect_cb = FCCheckBox() + grid0.addWidget(self.ncc_connect_cb, 4, 1) + + contourlabel = QtWidgets.QLabel("Contour:") + contourlabel.setToolTip( + "Cut around the perimeter of the polygon\n" + "to trim rough edges." + ) + grid0.addWidget(contourlabel, 5, 0) + self.ncc_contour_cb = FCCheckBox() + grid0.addWidget(self.ncc_contour_cb, 5, 1) + + restlabel = QtWidgets.QLabel("Rest M.:") + restlabel.setToolTip( + "If checked, use 'rest machining'.\n" + "Basically it will clear copper outside PCB features,\n" + "using the biggest tool and continue with the next tools,\n" + "from bigger to smaller, to clear areas of copper that\n" + "could not be cleared by previous tool.\n" + "If not checked, use the standard algorithm." + ) + grid0.addWidget(restlabel, 6, 0) + self.ncc_rest_cb = FCCheckBox() + grid0.addWidget(self.ncc_rest_cb, 6, 1) + + self.layout.addStretch() + + +class ToolsCutoutPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent) + super(ToolsCutoutPrefGroupUI, self).__init__(self) + + self.setTitle(str("Cutout Tool Options")) + + ## Board cuttout + self.board_cutout_label = QtWidgets.QLabel("Board cutout:") + self.board_cutout_label.setToolTip( + "Create toolpaths to cut around\n" + "the PCB and separate it from\n" + "the original board." + ) + self.layout.addWidget(self.board_cutout_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + tdclabel = QtWidgets.QLabel('Tool dia:') + tdclabel.setToolTip( + "Diameter of the cutting tool." + ) + grid0.addWidget(tdclabel, 0, 0) + self.cutout_tooldia_entry = LengthEntry() + grid0.addWidget(self.cutout_tooldia_entry, 0, 1) + + marginlabel = QtWidgets.QLabel('Margin:') + marginlabel.setToolTip( + "Distance from objects at which\n" + "to draw the cutout." + ) + grid0.addWidget(marginlabel, 1, 0) + self.cutout_margin_entry = LengthEntry() + grid0.addWidget(self.cutout_margin_entry, 1, 1) + + gaplabel = QtWidgets.QLabel('Gap size:') + gaplabel.setToolTip( + "Size of the gaps in the toolpath\n" + "that will remain to hold the\n" + "board in place." + ) + grid0.addWidget(gaplabel, 2, 0) + self.cutout_gap_entry = LengthEntry() + grid0.addWidget(self.cutout_gap_entry, 2, 1) + + gapslabel = QtWidgets.QLabel('Gaps:') + gapslabel.setToolTip( + "Where to place the gaps, Top/Bottom\n" + "Left/Rigt, or on all 4 sides." + ) + grid0.addWidget(gapslabel, 3, 0) + self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'}, + {'label': '2 (L/R)', 'value': 'lr'}, + {'label': '4', 'value': '4'}]) + grid0.addWidget(self.gaps_radio, 3, 1) + + self.layout.addStretch() + + class FlatCAMActivityView(QtWidgets.QWidget): def __init__(self, parent=None): diff --git a/README.md b/README.md index c00eb022..8ec6d495 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ CAD program, and create G-Code for Isolation routing. - added shortcut key 'L' for creating 'New Excellon' - added shortcut key combo 'SHIFT+S' for Running a Script. - modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M02 (laser active) whenever a value is enter in the Spindlespeed entry field +- remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS + 26.01.2019 diff --git a/flatcamTools/ToolCutout.py b/flatcamTools/ToolCutout.py index c7825b62..3a24a54c 100644 --- a/flatcamTools/ToolCutout.py +++ b/flatcamTools/ToolCutout.py @@ -146,8 +146,8 @@ class ToolCutout(FlatCAMTool): "- one gap Left / one gap Right\n" "- one gap on each of the 4 sides." ) - self.gaps_rect_radio = RadioSet([{'label': 'T/B', 'value': 'tb'}, - {'label': 'L/R', 'value': 'lr'}, + self.gaps_rect_radio = RadioSet([{'label': '2(T/B)', 'value': 'tb'}, + {'label': '2(L/R)', 'value': 'lr'}, {'label': '4', 'value': '4'}]) form_layout_3.addRow(gapslabel_rect, self.gaps_rect_radio) @@ -186,11 +186,19 @@ class ToolCutout(FlatCAMTool): def run(self): FlatCAMTool.run(self) + self.set_ui() self.app.ui.notebook.setTabText(2, "Cutout Tool") def install(self, icon=None, separator=None, **kwargs): FlatCAMTool.install(self, icon, separator, shortcut='ALT+U', **kwargs) + def set_ui(self): + self.dia.set_value(float(self.app.defaults["tools_cutouttooldia"])) + self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"])) + self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"])) + self.gaps.set_value(4) + self.gaps_rect_radio.set_value(str(self.app.defaults["tools_gaps_rect"])) + def on_freeform_cutout(self): def subtract_rectangle(obj_, x0, y0, x1, y1): diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 4782a7bd..a603ffe7 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -244,12 +244,12 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.ui.notebook.setTabText(2, "NCC Tool") def set_ui(self): - self.ncc_overlap_entry.set_value(self.app.defaults["gerber_nccoverlap"]) - self.ncc_margin_entry.set_value(self.app.defaults["gerber_nccmargin"]) - self.ncc_method_radio.set_value(self.app.defaults["gerber_nccmethod"]) - self.ncc_connect_cb.set_value(self.app.defaults["gerber_nccconnect"]) - self.ncc_contour_cb.set_value(self.app.defaults["gerber_ncccontour"]) - self.ncc_rest_cb.set_value(self.app.defaults["gerber_nccrest"]) + self.ncc_overlap_entry.set_value(self.app.defaults["tools_nccoverlap"]) + self.ncc_margin_entry.set_value(self.app.defaults["tools_nccmargin"]) + self.ncc_method_radio.set_value(self.app.defaults["tools_nccmethod"]) + self.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"]) + self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"]) + self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"]) self.tools_table.setupContextMenu() self.tools_table.addContextMenu( @@ -289,18 +289,18 @@ class NonCopperClear(FlatCAMTool, Gerber): "pathconnect": self.app.defaults["geometry_pathconnect"], "paintcontour": self.app.defaults["geometry_paintcontour"], "paintoverlap": self.app.defaults["geometry_paintoverlap"], - "nccoverlap": self.app.defaults["gerber_nccoverlap"], - "nccmargin": self.app.defaults["gerber_nccmargin"], - "nccmethod": self.app.defaults["gerber_nccmethod"], - "nccconnect": self.app.defaults["gerber_nccconnect"], - "ncccontour": self.app.defaults["gerber_ncccontour"], - "nccrest": self.app.defaults["gerber_nccrest"] + "nccoverlap": self.app.defaults["tools_nccoverlap"], + "nccmargin": self.app.defaults["tools_nccmargin"], + "nccmethod": self.app.defaults["tools_nccmethod"], + "nccconnect": self.app.defaults["tools_nccconnect"], + "ncccontour": self.app.defaults["tools_ncccontour"], + "nccrest": self.app.defaults["tools_nccrest"] }) try: dias = [float(eval(dia)) for dia in self.app.defaults["gerber_ncctools"].split(",")] except: - log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> Gerber Object -> NCC Tools.") + log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> TOOLS -> NCC Tools.") return self.tooluid = 0 @@ -550,22 +550,22 @@ class NonCopperClear(FlatCAMTool, Gerber): def on_ncc(self): over = self.ncc_overlap_entry.get_value() - over = over if over else self.app.defaults["gerber_nccoverlap"] + over = over if over else self.app.defaults["tools_nccoverlap"] margin = self.ncc_margin_entry.get_value() - margin = margin if margin else self.app.defaults["gerber_nccmargin"] + margin = margin if margin else self.app.defaults["tools_nccmargin"] connect = self.ncc_connect_cb.get_value() - connect = connect if connect else self.app.defaults["gerber_nccconnect"] + connect = connect if connect else self.app.defaults["tools_nccconnect"] contour = self.ncc_contour_cb.get_value() - contour = contour if contour else self.app.defaults["gerber_ncccontour"] + contour = contour if contour else self.app.defaults["tools_ncccontour"] clearing_method = self.ncc_rest_cb.get_value() - clearing_method = clearing_method if clearing_method else self.app.defaults["gerber_nccrest"] + clearing_method = clearing_method if clearing_method else self.app.defaults["tools_nccrest"] pol_method = self.ncc_method_radio.get_value() - pol_method = pol_method if pol_method else self.app.defaults["gerber_nccmethod"] + pol_method = pol_method if pol_method else self.app.defaults["tools_nccmethod"] self.obj_name = self.object_combo.currentText() # Get source object. From 8145ff9aac8b01f891a3b1cd6f4253e7816f6732 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 28 Jan 2019 02:01:53 +0200 Subject: [PATCH 11/23] - fixed the FlatCAMGerber.merge() function - added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object --- FlatCAMApp.py | 21 +++++++++++++++++++++ FlatCAMGUI.py | 10 ++++++++-- FlatCAMObj.py | 8 +++++--- README.md | 7 ++++++- camlib.py | 9 +++++++-- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 5fd18e02..b550d4ac 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -937,6 +937,8 @@ class App(QtCore.QObject): self.ui.menuedit_convertjoin.triggered.connect(self.on_edit_join) self.ui.menuedit_convertjoinexc.triggered.connect(self.on_edit_join_exc) + self.ui.menuedit_convertjoingrb.triggered.connect(self.on_edit_join_grb) + self.ui.menuedit_convert_sg2mg.triggered.connect(self.on_convert_singlegeo_to_multigeo) self.ui.menuedit_convert_mg2sg.triggered.connect(self.on_convert_multigeo_to_singlegeo) @@ -2485,6 +2487,25 @@ class App(QtCore.QObject): self.new_object("excellon", 'Combo_Excellon', initialize) + def on_edit_join_grb(self): + """ + Callback for Edit->Join Gerber. Joins the selected Gerber objects into + a new one. + + :return: None + """ + objs = self.collection.get_selected() + + for obj in objs: + if not isinstance(obj, FlatCAMGerber): + self.inform.emit("[error_notcl]Failed. Gerber joining works only on Gerber objects.") + return + + def initialize(obj, app): + FlatCAMGerber.merge(objs, obj) + + self.new_object("gerber", 'Combo_Gerber', initialize) + def on_convert_singlegeo_to_multigeo(self): obj = self.collection.get_active() diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 016ebd9b..269dcdce 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -159,7 +159,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # Separator self.menuedit.addSeparator() self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Object\tE') - self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), '&Update Object\tCTRL+S') + self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Save && Close Editor\tCTRL+S') # Separator self.menuedit.addSeparator() self.menuedit_convert = self.menuedit.addMenu(QtGui.QIcon('share/convert24.png'), 'Conversion') @@ -175,6 +175,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon('share/join16.png'), 'Join Excellon(s) -> Excellon') self.menuedit_convertjoinexc.setToolTip( "Merge a selection of Excellon objects into a new combo Excellon object.") + self.menuedit_convertjoingrb = self.menuedit_convert.addAction( + QtGui.QIcon('share/join16.png'), 'Join Gerber(s) -> Gerber') + self.menuedit_convertjoingrb.setToolTip( + "Merge a selection of Gerber objects into a new combo Gerber object.") # Separator self.menuedit_convert.addSeparator() self.menuedit_convert_sg2mg = self.menuedit_convert.addAction( @@ -372,7 +376,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_exc32.png'), "New Blank Excellon") self.toolbargeo.addSeparator() self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), "Editor") - self.update_obj_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit_ok32_bis.png'), "Save Object") + self.update_obj_btn = self.toolbargeo.addAction( + QtGui.QIcon('share/edit_ok32_bis.png'), "Save Object and close the Editor" + ) self.update_obj_btn.setEnabled(False) self.toolbargeo.addSeparator() self.delete_btn = self.toolbargeo.addAction(QtGui.QIcon('share/cancel_edit32.png'), "&Delete") diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 761f03a8..dbd1745c 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -366,6 +366,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if grb_final.solid_geometry is None: grb_final.solid_geometry = [] + if type(grb_final.solid_geometry) is not list: grb_final.solid_geometry = [grb_final.solid_geometry] @@ -380,10 +381,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # Expand lists if type(grb) is list: FlatCAMGerber.merge(grb, grb_final) + else: # If not list, just append + for geos in grb.solid_geometry: + grb_final.solid_geometry.append(geos) - # If not list, just append - else: - grb_final.solid_geometry.append(grb.solid_geometry) + grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry) def __init__(self, name): Gerber.__init__(self, steps_per_circle=self.app.defaults["gerber_circle_steps"]) diff --git a/README.md b/README.md index 8ec6d495..6101db65 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ CAD program, and create G-Code for Isolation routing. ================================================= +28.01.2018 + +- fixed the FlatCAMGerber.merge() function +- added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object + + 27.01.2018 - added more key shortcuts into the application; they are now displayed in the GUI menu's @@ -20,7 +26,6 @@ CAD program, and create G-Code for Isolation routing. - modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M02 (laser active) whenever a value is enter in the Spindlespeed entry field - remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS - 26.01.2019 - fixed grbl_11 postprocessor in linear_code() function diff --git a/camlib.py b/camlib.py index 9d65a705..ea9f2646 100644 --- a/camlib.py +++ b/camlib.py @@ -2965,7 +2965,7 @@ class Gerber (Geometry): return 0, 0, 0, 0 def bounds_rec(obj): - if type(obj) is list: + if type(obj) is list and type(obj) is not MultiPolygon: minx = Inf miny = Inf maxx = -Inf @@ -2980,7 +2980,12 @@ class Gerber (Geometry): maxx = max(maxx, maxx_) maxy = max(maxy, maxy_) else: - minx_, miny_, maxx_, maxy_ = bounds_rec(k) + try: + minx_, miny_, maxx_, maxy_ = bounds_rec(k) + except Exception as e: + log.debug("camlib.Geometry.bounds() --> %s" % str(e)) + return + minx = min(minx, minx_) miny = min(miny, miny_) maxx = max(maxx, maxx_) From 37e7c8167d1d83d22c9f35eb57fe4ebe467a8c05 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 28 Jan 2019 02:47:53 +0200 Subject: [PATCH 12/23] - fixed the FlatCAMGerber.merge() function - added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object --- FlatCAMApp.py | 136 ++++++++++--------- FlatCAMGUI.py | 210 ++++++++++++++++------------- README.md | 4 +- flatcamTools/ToolNonCopperClear.py | 18 +-- flatcamTools/ToolPaint.py | 15 ++- 5 files changed, 210 insertions(+), 173 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index b550d4ac..e53d5d24 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -354,6 +354,7 @@ class App(QtCore.QObject): "excellon_tooldia": self.excellon_defaults_form.excellon_opt_group.tooldia_entry, "excellon_slot_tooldia": self.excellon_defaults_form.excellon_opt_group.slot_tooldia_entry, "excellon_gcode_type": self.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio, + "geometry_plot": self.geometry_defaults_form.geometry_group.plot_cb, "geometry_segx": self.geometry_defaults_form.geometry_group.segx_entry, "geometry_segy": self.geometry_defaults_form.geometry_group.segy_entry, @@ -363,16 +364,9 @@ class App(QtCore.QObject): "geometry_feedrate_z": self.geometry_defaults_form.geometry_group.cncplunge_entry, "geometry_feedrate_rapid": self.geometry_defaults_form.geometry_group.cncfeedrate_rapid_entry, "geometry_cnctooldia": self.geometry_defaults_form.geometry_group.cnctooldia_entry, - "geometry_painttooldia": self.geometry_defaults_form.geometry_group.painttooldia_entry, "geometry_spindlespeed": self.geometry_defaults_form.geometry_group.cncspindlespeed_entry, "geometry_dwell": self.geometry_defaults_form.geometry_group.dwell_cb, "geometry_dwelltime": self.geometry_defaults_form.geometry_group.dwelltime_entry, - "geometry_paintoverlap": self.geometry_defaults_form.geometry_group.paintoverlap_entry, - "geometry_paintmargin": self.geometry_defaults_form.geometry_group.paintmargin_entry, - "geometry_paintmethod": self.geometry_defaults_form.geometry_group.paintmethod_combo, - "geometry_selectmethod": self.geometry_defaults_form.geometry_group.selectmethod_combo, - "geometry_pathconnect": self.geometry_defaults_form.geometry_group.pathconnect_cb, - "geometry_paintcontour": self.geometry_defaults_form.geometry_group.contour_cb, "geometry_ppname_g": self.geometry_defaults_form.geometry_group.pp_geometry_name_cb, "geometry_toolchange": self.geometry_defaults_form.geometry_group.toolchange_cb, "geometry_toolchangez": self.geometry_defaults_form.geometry_group.toolchangez_entry, @@ -383,6 +377,7 @@ class App(QtCore.QObject): "geometry_depthperpass": self.geometry_defaults_form.geometry_group.depthperpass_entry, "geometry_extracut": self.geometry_defaults_form.geometry_group.extracut_cb, "geometry_circle_steps": self.geometry_defaults_form.geometry_group.circle_steps_entry, + "cncjob_plot": self.cncjob_defaults_form.cncjob_group.plot_cb, "cncjob_tooldia": self.cncjob_defaults_form.cncjob_group.tooldia_entry, "cncjob_coords_decimals": self.cncjob_defaults_form.cncjob_group.coords_dec_entry, @@ -402,7 +397,15 @@ class App(QtCore.QObject): "tools_cutouttooldia": self.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry, "tools_cutoutmargin": self.tools_defaults_form.tools_cutout_group.cutout_margin_entry, "tools_cutoutgapsize": self.tools_defaults_form.tools_cutout_group.cutout_gap_entry, - "tools_gaps_rect": self.tools_defaults_form.tools_cutout_group.gaps_radio + "tools_gaps_rect": self.tools_defaults_form.tools_cutout_group.gaps_radio, + + "tools_painttooldia": self.tools_defaults_form.tools_paint_group.painttooldia_entry, + "tools_paintoverlap": self.tools_defaults_form.tools_paint_group.paintoverlap_entry, + "tools_paintmargin": self.tools_defaults_form.tools_paint_group.paintmargin_entry, + "tools_paintmethod": self.tools_defaults_form.tools_paint_group.paintmethod_combo, + "tools_selectmethod": self.tools_defaults_form.tools_paint_group.selectmethod_combo, + "tools_pathconnect": self.tools_defaults_form.tools_paint_group.pathconnect_cb, + "tools_paintcontour": self.tools_defaults_form.tools_paint_group.contour_cb } # loads postprocessors self.postprocessors = load_postprocessors(self) @@ -439,6 +442,35 @@ class App(QtCore.QObject): "global_workspaceT": "A4P", "global_toolbar_view": 31, + "global_background_timeout": 300000, # Default value is 5 minutes + "global_verbose_error_level": 0, # Shell verbosity 0 = default + # (python trace only for unknown errors), + # 1 = show trace(show trace allways), + # 2 = (For the future). + + # Persistence + "global_last_folder": None, + "global_last_save_folder": None, + + # Default window geometry + "global_def_win_x": 100, + "global_def_win_y": 100, + "global_def_win_w": 1024, + "global_def_win_h": 650, + + # Constants... + "global_defaults_save_period_ms": 20000, # Time between default saves. + "global_shell_shape": [500, 300], # Shape of the shell in pixels. + "global_shell_at_startup": False, # Show the shell at startup. + "global_recent_limit": 10, # Max. items in recent list. + "fit_key": '1', + "zoom_out_key": '2', + "zoom_in_key": '3', + "grid_toggle_key": 'G', + "zoom_ratio": 1.5, + "global_point_clipboard_format": "(%.4f, %.4f)", + "global_zdownrate": None, + "gerber_plot": True, "gerber_solid": True, "gerber_multicolored": False, @@ -448,14 +480,15 @@ class App(QtCore.QObject): "gerber_combine_passes": False, "gerber_milling_type": "cl", - "gerber_noncoppermargin": 0.0, + "gerber_noncoppermargin": 0.1, "gerber_noncopperrounded": False, - "gerber_bboxmargin": 0.0, + "gerber_bboxmargin": 0.1, "gerber_bboxrounded": False, "gerber_circle_steps": 64, + "gerber_use_buffer_for_union": True, "excellon_plot": True, - "excellon_solid": False, + "excellon_solid": True, "excellon_format_upper_in": 2, "excellon_format_lower_in": 4, "excellon_format_upper_mm": 3, @@ -499,13 +532,6 @@ class App(QtCore.QObject): "geometry_spindlespeed": None, "geometry_dwell": False, "geometry_dwelltime": 1, - "geometry_painttooldia": 0.07, - "geometry_paintoverlap": 0.15, - "geometry_paintmargin": 0.0, - "geometry_paintmethod": "seed", - "geometry_selectmethod": "single", - "geometry_pathconnect": True, - "geometry_paintcontour": True, "geometry_ppname_g": 'default', "geometry_depthperpass": 0.002, "geometry_multidepth": False, @@ -522,45 +548,25 @@ class App(QtCore.QObject): "tools_ncctools": "1.0, 0.5", "tools_nccoverlap": 0.4, - "tools_nccmargin": 1, + "tools_nccmargin": 0.1, "tools_nccmethod": "seed", "tools_nccconnect": True, "tools_ncccontour": True, "tools_nccrest": False, - "tools_cutouttooldia": 0.07, + + "tools_cutouttooldia": 0.1, "tools_cutoutmargin": 0.1, "tools_cutoutgapsize": 0.15, "tools_gaps_rect": "4", - "global_background_timeout": 300000, # Default value is 5 minutes - "global_verbose_error_level": 0, # Shell verbosity 0 = default - # (python trace only for unknown errors), - # 1 = show trace(show trace allways), - # 2 = (For the future). + "tools_painttooldia": 0.07, + "tools_paintoverlap": 0.15, + "tools_paintmargin": 0.0, + "tools_paintmethod": "seed", + "tools_selectmethod": "single", + "tools_pathconnect": True, + "tools_paintcontour": True - # Persistence - "global_last_folder": None, - "global_last_save_folder": None, - - # Default window geometry - "global_def_win_x": 100, - "global_def_win_y": 100, - "global_def_win_w": 1024, - "global_def_win_h": 650, - - # Constants... - "global_defaults_save_period_ms": 20000, # Time between default saves. - "global_shell_shape": [500, 300], # Shape of the shell in pixels. - "global_shell_at_startup": False, # Show the shell at startup. - "global_recent_limit": 10, # Max. items in recent list. - "fit_key": '1', - "zoom_out_key": '2', - "zoom_in_key": '3', - "grid_toggle_key": 'G', - "zoom_ratio": 1.5, - "global_point_clipboard_format": "(%.4f, %.4f)", - "global_zdownrate": None, - "gerber_use_buffer_for_union": True }) ############################### @@ -648,10 +654,6 @@ class App(QtCore.QObject): "geometry_dwell": self.geometry_options_form.geometry_group.dwell_cb, "geometry_dwelltime": self.geometry_options_form.geometry_group.dwelltime_entry, "geometry_cnctooldia": self.geometry_options_form.geometry_group.cnctooldia_entry, - "geometry_painttooldia": self.geometry_options_form.geometry_group.painttooldia_entry, - "geometry_paintoverlap": self.geometry_options_form.geometry_group.paintoverlap_entry, - "geometry_paintmargin": self.geometry_options_form.geometry_group.paintmargin_entry, - "geometry_selectmethod": self.geometry_options_form.geometry_group.selectmethod_combo, "geometry_ppname_g": self.geometry_options_form.geometry_group.pp_geometry_name_cb, "geometry_toolchange": self.geometry_options_form.geometry_group.toolchange_cb, "geometry_toolchangez": self.geometry_options_form.geometry_group.toolchangez_entry, @@ -675,6 +677,14 @@ class App(QtCore.QObject): "tools_cutoutmargin": self.tools_options_form.tools_cutout_group.cutout_margin_entry, "tools_cutoutgapsize": self.tools_options_form.tools_cutout_group.cutout_gap_entry, "tools_gaps": self.tools_options_form.tools_cutout_group.gaps_radio, + + "tools_painttooldia": self.tools_options_form.tools_paint_group.painttooldia_entry, + "tools_paintoverlap": self.tools_options_form.tools_paint_group.paintoverlap_entry, + "tools_paintmargin": self.tools_options_form.tools_paint_group.paintmargin_entry, + "tools_paintmethod": self.tools_options_form.tools_paint_group.paintmethod_combo, + "tools_selectmethod": self.tools_options_form.tools_paint_group.selectmethod_combo, + "tools_pathconnect": self.tools_options_form.tools_paint_group.pathconnect_cb, + "tools_paintcontour": self.tools_options_form.tools_paint_group.contour_cb } for name in list(self.postprocessors.keys()): @@ -687,6 +697,11 @@ class App(QtCore.QObject): "units": "IN", "global_gridx": 1.0, "global_gridy": 1.0, + "global_background_timeout": 300000, # Default value is 5 minutes + "global_verbose_error_level": 0, # Shell verbosity: + # 0 = default(python trace only for unknown errors), + # 1 = show trace(show trace allways), 2 = (For the future). + "gerber_plot": True, "gerber_solid": True, "gerber_multicolored": False, @@ -698,6 +713,7 @@ class App(QtCore.QObject): "gerber_noncopperrounded": False, "gerber_bboxmargin": 0.0, "gerber_bboxrounded": False, + "excellon_plot": True, "excellon_solid": False, "excellon_format_upper_in": 2, @@ -736,10 +752,6 @@ class App(QtCore.QObject): "geometry_dwell": True, "geometry_dwelltime": 1000, "geometry_cnctooldia": 0.016, - "geometry_painttooldia": 0.07, - "geometry_paintoverlap": 0.15, - "geometry_paintmargin": 0.0, - "geometry_selectmethod": "single", "geometry_toolchange": False, "geometry_toolchangez": 2.0, "geometry_toolchangexy": "0.0, 0.0", @@ -763,10 +775,14 @@ class App(QtCore.QObject): "tools_cutoutgapsize": 0.15, "tools_gaps": "4", - "global_background_timeout": 300000, # Default value is 5 minutes - "global_verbose_error_level": 0, # Shell verbosity: - # 0 = default(python trace only for unknown errors), - # 1 = show trace(show trace allways), 2 = (For the future). + "tools_painttooldia": 0.07, + "tools_paintoverlap": 0.15, + "tools_paintmargin": 0.0, + "tools_paintmethod": "seed", + "tools_selectmethod": "single", + "tools_pathconnect": True, + "tools_paintcontour": True + }) self.options.update(self.defaults) # Copy app defaults to project options diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 269dcdce..6ad444ad 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -960,10 +960,18 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.tools_ncc_group = ToolsNCCPrefGroupUI() self.tools_ncc_group.setFixedWidth(260) + self.tools_paint_group = ToolsPaintPrefGroupUI() + self.tools_paint_group.setFixedWidth(260) + self.tools_cutout_group = ToolsCutoutPrefGroupUI() self.tools_cutout_group.setFixedWidth(260) - self.layout.addWidget(self.tools_ncc_group) + self.vlay = QtWidgets.QVBoxLayout() + self.vlay.addWidget(self.tools_ncc_group) + self.vlay.addWidget(self.tools_paint_group) + + self.layout.addLayout(self.vlay) + self.layout.addWidget(self.tools_cutout_group) self.layout.addStretch() @@ -2187,101 +2195,6 @@ class GeometryPrefGroupUI(OptionsGroupUI): self.segy_entry = FCEntry() grid2.addWidget(self.segy_entry, 18, 1) - # ------------------------------ - ## Paint area - # ------------------------------ - self.paint_label = QtWidgets.QLabel('Paint Area:') - self.paint_label.setToolTip( - "Creates tool paths to cover the\n" - "whole area of a polygon (remove\n" - "all copper). You will be asked\n" - "to click on the desired polygon." - ) - self.layout.addWidget(self.paint_label) - - grid4 = QtWidgets.QGridLayout() - self.layout.addLayout(grid4) - - # Tool dia - ptdlabel = QtWidgets.QLabel('Tool dia:') - ptdlabel.setToolTip( - "Diameter of the tool to\n" - "be used in the operation." - ) - grid4.addWidget(ptdlabel, 0, 0) - - self.painttooldia_entry = LengthEntry() - grid4.addWidget(self.painttooldia_entry, 0, 1) - - # Overlap - ovlabel = QtWidgets.QLabel('Overlap:') - ovlabel.setToolTip( - "How much (fraction) of the tool\n" - "width to overlap each tool pass." - ) - grid4.addWidget(ovlabel, 1, 0) - self.paintoverlap_entry = LengthEntry() - grid4.addWidget(self.paintoverlap_entry, 1, 1) - - # Margin - marginlabel = QtWidgets.QLabel('Margin:') - marginlabel.setToolTip( - "Distance by which to avoid\n" - "the edges of the polygon to\n" - "be painted." - ) - grid4.addWidget(marginlabel, 2, 0) - self.paintmargin_entry = LengthEntry() - grid4.addWidget(self.paintmargin_entry, 2, 1) - - # Method - methodlabel = QtWidgets.QLabel('Method:') - methodlabel.setToolTip( - "Algorithm to paint the polygon:
" - "Standard: Fixed step inwards.
" - "Seed-based: Outwards from seed." - ) - grid4.addWidget(methodlabel, 3, 0) - self.paintmethod_combo = RadioSet([ - {"label": "Standard", "value": "standard"}, - {"label": "Seed-based", "value": "seed"}, - {"label": "Straight lines", "value": "lines"} - ], orientation='vertical', stretch=False) - grid4.addWidget(self.paintmethod_combo, 3, 1) - - # Connect lines - pathconnectlabel = QtWidgets.QLabel("Connect:") - pathconnectlabel.setToolTip( - "Draw lines between resulting\n" - "segments to minimize tool lifts." - ) - grid4.addWidget(pathconnectlabel, 4, 0) - self.pathconnect_cb = FCCheckBox() - grid4.addWidget(self.pathconnect_cb, 4, 1) - - # Paint contour - contourlabel = QtWidgets.QLabel("Contour:") - contourlabel.setToolTip( - "Cut around the perimeter of the polygon\n" - "to trim rough edges." - ) - grid4.addWidget(contourlabel, 5, 0) - self.contour_cb = FCCheckBox() - grid4.addWidget(self.contour_cb, 5, 1) - - # Polygon selection - selectlabel = QtWidgets.QLabel('Selection:') - selectlabel.setToolTip( - "How to select the polygons to paint." - ) - grid4.addWidget(selectlabel, 6, 0) - self.selectmethod_combo = RadioSet([ - {"label": "Single", "value": "single"}, - {"label": "All", "value": "all"}, - # {"label": "Rectangle", "value": "rectangle"} - ]) - grid4.addWidget(self.selectmethod_combo, 6, 1) - self.layout.addStretch() @@ -2543,6 +2456,111 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class ToolsPaintPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Paint Area Tool Options", parent=parent) + super(ToolsPaintPrefGroupUI, self).__init__(self) + + self.setTitle(str("Paint Area Tool Options")) + + # ------------------------------ + ## Paint area + # ------------------------------ + self.paint_label = QtWidgets.QLabel('Paint Area:') + self.paint_label.setToolTip( + "Creates tool paths to cover the\n" + "whole area of a polygon (remove\n" + "all copper). You will be asked\n" + "to click on the desired polygon." + ) + self.layout.addWidget(self.paint_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + # Tool dia + ptdlabel = QtWidgets.QLabel('Tool dia:') + ptdlabel.setToolTip( + "Diameter of the tool to\n" + "be used in the operation." + ) + grid0.addWidget(ptdlabel, 0, 0) + + self.painttooldia_entry = LengthEntry() + grid0.addWidget(self.painttooldia_entry, 0, 1) + + # Overlap + ovlabel = QtWidgets.QLabel('Overlap:') + ovlabel.setToolTip( + "How much (fraction) of the tool\n" + "width to overlap each tool pass." + ) + grid0.addWidget(ovlabel, 1, 0) + self.paintoverlap_entry = LengthEntry() + grid0.addWidget(self.paintoverlap_entry, 1, 1) + + # Margin + marginlabel = QtWidgets.QLabel('Margin:') + marginlabel.setToolTip( + "Distance by which to avoid\n" + "the edges of the polygon to\n" + "be painted." + ) + grid0.addWidget(marginlabel, 2, 0) + self.paintmargin_entry = LengthEntry() + grid0.addWidget(self.paintmargin_entry, 2, 1) + + # Method + methodlabel = QtWidgets.QLabel('Method:') + methodlabel.setToolTip( + "Algorithm to paint the polygon:
" + "Standard: Fixed step inwards.
" + "Seed-based: Outwards from seed." + ) + grid0.addWidget(methodlabel, 3, 0) + self.paintmethod_combo = RadioSet([ + {"label": "Standard", "value": "standard"}, + {"label": "Seed-based", "value": "seed"}, + {"label": "Straight lines", "value": "lines"} + ], orientation='vertical', stretch=False) + grid0.addWidget(self.paintmethod_combo, 3, 1) + + # Connect lines + pathconnectlabel = QtWidgets.QLabel("Connect:") + pathconnectlabel.setToolTip( + "Draw lines between resulting\n" + "segments to minimize tool lifts." + ) + grid0.addWidget(pathconnectlabel, 4, 0) + self.pathconnect_cb = FCCheckBox() + grid0.addWidget(self.pathconnect_cb, 4, 1) + + # Paint contour + contourlabel = QtWidgets.QLabel("Contour:") + contourlabel.setToolTip( + "Cut around the perimeter of the polygon\n" + "to trim rough edges." + ) + grid0.addWidget(contourlabel, 5, 0) + self.contour_cb = FCCheckBox() + grid0.addWidget(self.contour_cb, 5, 1) + + # Polygon selection + selectlabel = QtWidgets.QLabel('Selection:') + selectlabel.setToolTip( + "How to select the polygons to paint." + ) + grid0.addWidget(selectlabel, 6, 0) + self.selectmethod_combo = RadioSet([ + {"label": "Single", "value": "single"}, + {"label": "All", "value": "all"}, + # {"label": "Rectangle", "value": "rectangle"} + ]) + grid0.addWidget(self.selectmethod_combo, 6, 1) + + self.layout.addStretch() + + class FlatCAMActivityView(QtWidgets.QWidget): def __init__(self, parent=None): diff --git a/README.md b/README.md index 6101db65..4cb52c9f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ CAD program, and create G-Code for Isolation routing. - fixed the FlatCAMGerber.merge() function - added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object - +- moved Paint Tool defaults from Geometry section to the Tools section in Edit -> Preferences 27.01.2018 @@ -23,7 +23,7 @@ CAD program, and create G-Code for Isolation routing. - added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) - added shortcut key 'L' for creating 'New Excellon' - added shortcut key combo 'SHIFT+S' for Running a Script. -- modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M02 (laser active) whenever a value is enter in the Spindlespeed entry field +- modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M03 (laser active) whenever a value is enter in the Spindlespeed entry field - remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS 26.01.2019 diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index a603ffe7..7cd4bd70 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -263,7 +263,6 @@ class NonCopperClear(FlatCAMTool, Gerber): self.default_data.update({ "name": '_ncc', "plot": self.app.defaults["geometry_plot"], - "tooldia": self.app.defaults["geometry_painttooldia"], "cutz": self.app.defaults["geometry_cutz"], "vtipdia": 0.1, "vtipangle": 30, @@ -283,12 +282,15 @@ class NonCopperClear(FlatCAMTool, Gerber): "spindlespeed": self.app.defaults["geometry_spindlespeed"], "toolchangexy": self.app.defaults["geometry_toolchangexy"], "startz": self.app.defaults["geometry_startz"], - "paintmargin": self.app.defaults["geometry_paintmargin"], - "paintmethod": self.app.defaults["geometry_paintmethod"], - "selectmethod": self.app.defaults["geometry_selectmethod"], - "pathconnect": self.app.defaults["geometry_pathconnect"], - "paintcontour": self.app.defaults["geometry_paintcontour"], - "paintoverlap": self.app.defaults["geometry_paintoverlap"], + + "tooldia": self.app.defaults["tools_painttooldia"], + "paintmargin": self.app.defaults["tools_paintmargin"], + "paintmethod": self.app.defaults["tools_paintmethod"], + "selectmethod": self.app.defaults["tools_selectmethod"], + "pathconnect": self.app.defaults["tools_pathconnect"], + "paintcontour": self.app.defaults["tools_paintcontour"], + "paintoverlap": self.app.defaults["tools_paintoverlap"], + "nccoverlap": self.app.defaults["tools_nccoverlap"], "nccmargin": self.app.defaults["tools_nccmargin"], "nccmethod": self.app.defaults["tools_nccmethod"], @@ -298,7 +300,7 @@ class NonCopperClear(FlatCAMTool, Gerber): }) try: - dias = [float(eval(dia)) for dia in self.app.defaults["gerber_ncctools"].split(",")] + dias = [float(eval(dia)) for dia in self.app.defaults["tools_ncctools"].split(",")] except: log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> TOOLS -> NCC Tools.") return diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 2a14645f..319ac576 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -250,7 +250,6 @@ class ToolPaint(FlatCAMTool, Gerber): self.default_data.update({ "name": '_paint', "plot": self.app.defaults["geometry_plot"], - "tooldia": self.app.defaults["geometry_painttooldia"], "cutz": self.app.defaults["geometry_cutz"], "vtipdia": 0.1, "vtipangle": 30, @@ -270,12 +269,14 @@ class ToolPaint(FlatCAMTool, Gerber): "spindlespeed": self.app.defaults["geometry_spindlespeed"], "toolchangexy": self.app.defaults["geometry_toolchangexy"], "startz": self.app.defaults["geometry_startz"], - "paintmargin": self.app.defaults["geometry_paintmargin"], - "paintmethod": self.app.defaults["geometry_paintmethod"], - "selectmethod": self.app.defaults["geometry_selectmethod"], - "pathconnect": self.app.defaults["geometry_pathconnect"], - "paintcontour": self.app.defaults["geometry_paintcontour"], - "paintoverlap": self.app.defaults["geometry_paintoverlap"] + + "tooldia": self.app.defaults["tools_painttooldia"], + "paintmargin": self.app.defaults["tools_paintmargin"], + "paintmethod": self.app.defaults["tools_paintmethod"], + "selectmethod": self.app.defaults["tools_selectmethod"], + "pathconnect": self.app.defaults["tools_pathconnect"], + "paintcontour": self.app.defaults["tools_paintcontour"], + "paintoverlap": self.app.defaults["tools_paintoverlap"] }) self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"] From c7a22e7ce5af29fc0599ba007a2df8c2f4f18618 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 28 Jan 2019 02:53:33 +0200 Subject: [PATCH 13/23] - wip --- README.md | 1 + flatcamTools/ToolPaint.py | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4cb52c9f..77e2c457 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ CAD program, and create G-Code for Isolation routing. - added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object - moved Paint Tool defaults from Geometry section to the Tools section in Edit -> Preferences + 27.01.2018 - added more key shortcuts into the application; they are now displayed in the GUI menu's diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 319ac576..4e6de871 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -350,7 +350,6 @@ class ToolPaint(FlatCAMTool, Gerber): self.default_data.update({ "name": '_paint', "plot": self.app.defaults["geometry_plot"], - "tooldia": self.app.defaults["geometry_painttooldia"], "cutz": self.app.defaults["geometry_cutz"], "vtipdia": 0.1, "vtipangle": 30, @@ -370,17 +369,19 @@ class ToolPaint(FlatCAMTool, Gerber): "spindlespeed": self.app.defaults["geometry_spindlespeed"], "toolchangexy": self.app.defaults["geometry_toolchangexy"], "startz": self.app.defaults["geometry_startz"], - "paintmargin": self.app.defaults["geometry_paintmargin"], - "paintmethod": self.app.defaults["geometry_paintmethod"], - "selectmethod": self.app.defaults["geometry_selectmethod"], - "pathconnect": self.app.defaults["geometry_pathconnect"], - "paintcontour": self.app.defaults["geometry_paintcontour"], - "paintoverlap": self.app.defaults["geometry_paintoverlap"] + + "tooldia": self.app.defaults["tools_painttooldia"], + "paintmargin": self.app.defaults["tools_paintmargin"], + "paintmethod": self.app.defaults["tools_paintmethod"], + "selectmethod": self.app.defaults["tools_selectmethod"], + "pathconnect": self.app.defaults["tools_pathconnect"], + "paintcontour": self.app.defaults["tools_paintcontour"], + "paintoverlap": self.app.defaults["tools_paintoverlap"] }) # call on self.on_tool_add() counts as an call to self.build_ui() # through this, we add a initial row / tool in the tool_table - self.on_tool_add(self.app.defaults["geometry_painttooldia"], muted=True) + self.on_tool_add(self.app.defaults["tools_painttooldia"], muted=True) def build_ui(self): From e19af486b370b19fc5f32edc4484164d2cc3a89c Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 28 Jan 2019 03:24:06 +0200 Subject: [PATCH 14/23] - added key shortcuts for Open Manual = F1 and for Open Online VideoHelp = F2 --- FlatCAMApp.py | 15 +++++++++++++-- FlatCAMGUI.py | 6 +++--- ObjectCollection.py | 9 +++++++++ README.md | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index e53d5d24..46bdfaa8 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -3757,6 +3757,14 @@ class App(QtCore.QObject): return else: + if event.key == 'F1': + webbrowser.open(self.manual_url) + return + + if event.key == 'F2': + webbrowser.open(self.video_url) + return + if event.key == self.defaults['fit_key']: # 1 self.on_zoom_fit(None) return @@ -3896,8 +3904,11 @@ class App(QtCore.QObject): ALT+P: Paint Area Tool
ALT+R: Transformation Tool
ALT+U: Cutout PCB Tool
-ALT+Z: Panelize PCB Tool
-Del: Delete Obj''' +
+F1: Open Online Manual
+F2: Open Online Tutorials
+Del: Delete Obj +''' helpbox = QtWidgets.QMessageBox() helpbox.setText(msg) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 6ad444ad..cba08277 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -336,10 +336,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuhelp = self.menu.addMenu('&Help') self.menuhelp_about = self.menuhelp.addAction(QtGui.QIcon('share/tv16.png'), 'About FlatCAM') self.menuhelp_home = self.menuhelp.addAction(QtGui.QIcon('share/home16.png'), 'Home') - self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), 'Manual') + self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), 'Manual\tF1') self.menuhelp.addSeparator() - self.menuhelp_shortcut_list = self.menuhelp.addAction(QtGui.QIcon('share/shortcuts24.png'), 'Shortcuts List') - self.menuhelp_videohelp = self.menuhelp.addAction(QtGui.QIcon('share/videohelp24.png'), 'See on YouTube') + self.menuhelp_shortcut_list = self.menuhelp.addAction(QtGui.QIcon('share/shortcuts24.png'), 'Shortcuts List\t`') + self.menuhelp_videohelp = self.menuhelp.addAction(QtGui.QIcon('share/videohelp24.png'), 'See on YouTube\tF2') ################################ ### Project Tab Context menu ### diff --git a/ObjectCollection.py b/ObjectCollection.py index 2db3610b..9c955b5a 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -12,6 +12,7 @@ import inspect # TODO: Remove import FlatCAMApp from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtCore import Qt +import webbrowser class KeySensitiveListView(QtWidgets.QTreeView): @@ -345,6 +346,14 @@ class ObjectCollection(QtCore.QAbstractItemModel): return else: + # Open Manual + if key == QtCore.Qt.Key_F1: + webbrowser.open(self.app.manual_url) + + # Open Video Help + if key == QtCore.Qt.Key_F2: + webbrowser.open(self.app.video_url) + # Zoom Fit if key == QtCore.Qt.Key_1: self.app.on_zoom_fit(None) diff --git a/README.md b/README.md index 77e2c457..23e17b88 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ CAD program, and create G-Code for Isolation routing. - fixed the FlatCAMGerber.merge() function - added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object - moved Paint Tool defaults from Geometry section to the Tools section in Edit -> Preferences - +- added key shortcuts for Open Manual = F1 and for Open Online VideoHelp = F2 27.01.2018 From c80271935edc5c6ca8b3fd12a9757f4c60ff0284 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 28 Jan 2019 16:31:06 +0200 Subject: [PATCH 15/23] - minor changes --- FlatCAMGUI.py | 2 ++ FlatCAMObj.py | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index cba08277..6e6b85ae 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -569,6 +569,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.options_combo.addItem("PROJ. OPTIONS ") self.hlay1.addWidget(self.options_combo) + # disable this button as it may no longer be useful + self.options_combo.setVisible(False) self.hlay1.addStretch() self.general_scroll_area = VerticalScrollArea() diff --git a/FlatCAMObj.py b/FlatCAMObj.py index dbd1745c..fb16ca7a 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -404,9 +404,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber): "isooverlap": 0.15, "milling_type": "cl", "combine_passes": True, - "ncctools": "1.0, 0.5", - "nccoverlap": 0.4, - "nccmargin": 1, "noncoppermargin": 0.0, "noncopperrounded": False, "bboxmargin": 0.0, @@ -757,9 +754,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber): factor = Gerber.convert_units(self, units) self.options['isotooldia'] *= factor - self.options['cutoutmargin'] *= factor - self.options['cutoutgapsize'] *= factor - self.options['noncoppermargin'] *= factor self.options['bboxmargin'] *= factor def plot(self, **kwargs): From f6271033b3c87f133a7d66b7d54bb304bc9ebe06 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 01:03:21 +0200 Subject: [PATCH 16/23] - fixed issue in Tool Calculators when a float value was entered starting only with the dot. - added protection for entering incorrect values in Offset and Scale fields for Gerber and Geometry objects (in Selected Tab) --- FlatCAMApp.py | 2 +- FlatCAMObj.py | 21 ++++++++++++++++++++- README.md | 5 +++++ camlib.py | 22 ++++++++++++++++++++-- flatcamTools/ToolCalculators.py | 2 +- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 46bdfaa8..265d1d71 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -91,7 +91,7 @@ class App(QtCore.QObject): # Version version = 8.905 - version_date = "2019/01/28" + version_date = "2019/01/29" beta = True # URL for update checks and statistics diff --git a/FlatCAMObj.py b/FlatCAMObj.py index fb16ca7a..3c62f059 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -3470,8 +3470,20 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): :rtype: None """ + try: + xfactor = float(xfactor) + except: + self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.") + return + if yfactor is None: yfactor = xfactor + else: + try: + yfactor = float(yfactor) + except: + self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.") + return if point is None: px = 0 @@ -3489,6 +3501,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): else: self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor, origin=(px, py)) + self.app.inform.emit("[success]Geometry Scale done.") def offset(self, vect): """ @@ -3500,7 +3513,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): :rtype: None """ - dx, dy = vect + try: + dx, dy = vect + except TypeError: + self.app.inform.emit("[error_notcl]An (x,y) pair of values are needed. " + "Probable you entered only one value in the Offset field.") + return def translate_recursion(geom): if type(geom) == list: @@ -3516,6 +3534,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.tools[tool]['solid_geometry'] = translate_recursion(self.tools[tool]['solid_geometry']) else: self.solid_geometry=translate_recursion(self.solid_geometry) + self.app.inform.emit("[success]Geometry Offset done.") def convert_units(self, units): self.ui_disconnect() diff --git a/README.md b/README.md index 23e17b88..c16952f6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing. ================================================= +29.01.2019 + +- fixed issue in Tool Calculators when a float value was entered starting only with the dot. +- added protection for entering incorrect values in Offset and Scale fields for Gerber and Geometry objects (in Selected Tab) + 28.01.2018 - fixed the FlatCAMGerber.merge() function diff --git a/camlib.py b/camlib.py index ea9f2646..ef317ee6 100644 --- a/camlib.py +++ b/camlib.py @@ -3018,8 +3018,20 @@ class Gerber (Geometry): :rtype : None """ + try: + xfactor = float(xfactor) + except: + self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.") + return + if yfactor is None: yfactor = xfactor + else: + try: + yfactor = float(yfactor) + except: + self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.") + return if point is None: px = 0 @@ -3038,6 +3050,7 @@ class Gerber (Geometry): yfactor, origin=(px, py)) self.solid_geometry = scale_geom(self.solid_geometry) + self.app.inform.emit("[success]Gerber Scale done.") ## solid_geometry ??? # It's a cascaded union of objects. @@ -3066,8 +3079,12 @@ class Gerber (Geometry): :type vect: tuple :return: None """ - - dx, dy = vect + try: + dx, dy = vect + except TypeError: + self.app.inform.emit("[error_notcl]An (x,y) pair of values are needed. " + "Probable you entered only one value in the Offset field.") + return def offset_geom(obj): if type(obj) is list: @@ -3081,6 +3098,7 @@ class Gerber (Geometry): ## Solid geometry # self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy) self.solid_geometry = offset_geom(self.solid_geometry) + self.app.inform.emit("[success]Gerber Offset done.") def mirror(self, axis, point): """ diff --git a/flatcamTools/ToolCalculators.py b/flatcamTools/ToolCalculators.py index dc499972..b937a2e0 100644 --- a/flatcamTools/ToolCalculators.py +++ b/flatcamTools/ToolCalculators.py @@ -157,7 +157,7 @@ class ToolCalculator(FlatCAMTool): tip_diameter = float(self.tipDia_entry.get_value()) half_tip_angle = float(self.tipAngle_entry.get_value()) / 2 cut_depth = float(self.cutDepth_entry.get_value()) - except TypeError: + except: return tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle))) From 970b260333a4a8dc3aeb1ec5e9e6fbf3ebee8774 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 11:59:03 +0200 Subject: [PATCH 17/23] - added more shortcut keys in the Geometry Editor and in Excellon Editor; activated also the zoom (fit, in, out) shortcut keys ('1' , '2', '3') for the editors --- FlatCAMEditor.py | 83 +++++++++++++++++++++++++++++++++++++++++++++--- FlatCAMGUI.py | 6 ++-- README.md | 1 + 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/FlatCAMEditor.py b/FlatCAMEditor.py index e497db5d..fc33e3e4 100644 --- a/FlatCAMEditor.py +++ b/FlatCAMEditor.py @@ -2569,6 +2569,15 @@ class FlatCAMGeoEditor(QtCore.QObject): self.on_tool_select('rotate') self.active_tool.set_origin(self.snap(self.x, self.y)) + if event.key == '1': + self.app.on_zoom_fit(None) + + if event.key == '2': + self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y]) + + if event.key == '3': + self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y]) + # Arc Tool if event.key.name == 'A': self.select_tool('arc') @@ -2584,6 +2593,22 @@ class FlatCAMGeoEditor(QtCore.QObject): self.active_tool.set_origin(self.snap(self.x, self.y)) self.app.inform.emit("Click on target point.") + # Substract Tool + if event.key.name == 'E': + if self.get_selected() is not None: + self.intersection() + else: + msg = "Please select geometry items \n" \ + "on which to perform Intersection Tool." + + messagebox =QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle("Warning") + messagebox.setWindowIcon(QtGui.QIcon('share/warning.png')) + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + # Grid Snap if event.key.name == 'G': self.app.ui.grid_snap_btn.trigger() @@ -2622,14 +2647,42 @@ class FlatCAMGeoEditor(QtCore.QObject): if event.key.name == 'R': self.select_tool('rectangle') - # Select Tool + # Substract Tool if event.key.name == 'S': - self.select_tool('select') + if self.get_selected() is not None: + self.subtract() + else: + msg = "Please select geometry items \n" \ + "on which to perform Substraction Tool." + + messagebox =QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle("Warning") + messagebox.setWindowIcon(QtGui.QIcon('share/warning.png')) + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() # Add Text Tool if event.key.name == 'T': self.select_tool('text') + # Substract Tool + if event.key.name == 'U': + if self.get_selected() is not None: + self.union() + else: + msg = "Please select geometry items \n" \ + "on which to perform union." + + messagebox =QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle("Warning") + messagebox.setWindowIcon(QtGui.QIcon('share/warning.png')) + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + # Cut Action Tool if event.key.name == 'X': if self.get_selected() is not None: @@ -2662,11 +2715,15 @@ class FlatCAMGeoEditor(QtCore.QObject): def on_shortcut_list(self): msg = '''Shortcut list in Geometry Editor

+1: Zoom Fit
+2: Zoom Out
+3: Zoom In
A: Add an 'Arc'
B: Add a Buffer Geo
C: Copy Geo Item
+E: Intersection Tool
G: Grid Snap On/Off
-G: Paint Tool
+I: Paint Tool
K: Corner Snap On/Off
M: Move Geo Item

@@ -2674,8 +2731,9 @@ class FlatCAMGeoEditor(QtCore.QObject): O: Add a 'Circle'
P: Add a 'Path'
R: Add an 'Rectangle'
-S: Select Tool Active
+S: Substraction Tool
T: Add Text Geometry
+U: Union Tool

X: Cut Path

@@ -2683,7 +2741,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
Space: Rotate selected Geometry
Enter: Finish Current Action
-Escape: Abort Current Action
+Escape: Select Tool (Exit any other Tool)
Delete: Delete Obj''' helpbox =QtWidgets.QMessageBox() @@ -4756,6 +4814,18 @@ class FlatCAMExcEditor(QtCore.QObject): self.app.inform.emit("[warning_notcl]Cancelled. Nothing selected to delete.") return + if event.key == '1': + self.launched_from_shortcuts = True + self.app.on_zoom_fit(None) + + if event.key == '2': + self.launched_from_shortcuts = True + self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y]) + + if event.key == '3': + self.launched_from_shortcuts = True + self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y]) + # Add Array of Drill Hole Tool if event.key.name == 'A': self.launched_from_shortcuts = True @@ -4840,6 +4910,9 @@ class FlatCAMExcEditor(QtCore.QObject): def on_shortcut_list(self): msg = '''Shortcut list in Geometry Editor

+1: Zoom Fit
+2: Zoom Out
+3: Zoom In
A: Add an 'Drill Array'
C: Copy Drill Hole
D: Add an Drill Hole
diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 6e6b85ae..2de7edf9 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -294,11 +294,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.geo_editor_menu.addSeparator() self.geo_add_text_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/text32.png'), 'Add Text\tT') self.geo_editor_menu.addSeparator() - self.geo_union_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/union16.png'), 'Polygon Union') + self.geo_union_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/union16.png'), 'Polygon Union\tU') self.geo_intersection_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/intersection16.png'), - 'Polygon Intersection') + 'Polygon Intersection\tE') self.geo_subtract_menuitem = self.geo_editor_menu.addAction( - QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction' + QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction\tS' ) self.geo_editor_menu.addSeparator() self.geo_cutpath_menuitem = self.geo_editor_menu.addAction(QtGui.QIcon('share/cutpath16.png'), 'Cut Path\tX') diff --git a/README.md b/README.md index c16952f6..6c810bd1 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ CAD program, and create G-Code for Isolation routing. - fixed issue in Tool Calculators when a float value was entered starting only with the dot. - added protection for entering incorrect values in Offset and Scale fields for Gerber and Geometry objects (in Selected Tab) +- added more shortcut keys in the Geometry Editor and in Excellon Editor; activated also the zoom (fit, in, out) shortcut keys ('1' , '2', '3') for the editors 28.01.2018 From 32aa37d793dd05e53dcf255145583fb89708c5a7 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 15:18:41 +0200 Subject: [PATCH 18/23] - disabled the context menu in tools table on Paint Tool in case that the painting method is single. - added protection when trying to do Intersection in Geometry Editor without having selected Geometry items. --- FlatCAMEditor.py | 23 +++++++++++++++++++++-- README.md | 2 ++ flatcamTools/ToolPaint.py | 6 ++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/FlatCAMEditor.py b/FlatCAMEditor.py index fc33e3e4..49915e2f 100644 --- a/FlatCAMEditor.py +++ b/FlatCAMEditor.py @@ -613,6 +613,7 @@ class FCCircle(FCShapeTool): radius = distance(p1, p2) self.geometry = DrawToolShape(Point(p1).buffer(radius, int(self.steps_per_circ / 4))) self.complete = True + self.draw_app.app.inform.emit("[success]Done. Adding Circle completed.") class FCArc(FCShapeTool): @@ -794,6 +795,7 @@ class FCArc(FCShapeTool): self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle, self.direction, self.steps_per_circ))) self.complete = True + self.draw_app.app.inform.emit("[success]Done. Arc completed.") class FCRectangle(FCShapeTool): @@ -831,6 +833,7 @@ class FCRectangle(FCShapeTool): # self.geometry = LinearRing([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])]) self.geometry = DrawToolShape(Polygon([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])])) self.complete = True + self.draw_app.app.inform.emit("[success]Done. Rectangle completed.") class FCPolygon(FCShapeTool): @@ -869,6 +872,7 @@ class FCPolygon(FCShapeTool): self.geometry = DrawToolShape(Polygon(self.points)) self.draw_app.in_action = False self.complete = True + self.draw_app.app.inform.emit("[success]Done. Polygon completed.") def on_key(self, key): if key == 'backspace': @@ -885,6 +889,7 @@ class FCPath(FCPolygon): self.geometry = DrawToolShape(LineString(self.points)) self.draw_app.in_action = False self.complete = True + self.draw_app.app.inform.emit("[success]Done. Path completed.") def utility_geometry(self, data=None): if len(self.points) > 0: @@ -1174,6 +1179,7 @@ class FCMove(FCShapeTool): # self.draw_app.set_selected(g) self.complete = True + self.draw_app.app.inform.emit("[success]Done. Geometry(s) Move completed.") def utility_geometry(self, data=None): """ @@ -1209,6 +1215,7 @@ class FCCopy(FCMove): self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy)) for geom in self.draw_app.get_selected()] self.complete = True + self.draw_app.app.inform.emit("[success]Done. Geometry(s) Copy completed.") class FCText(FCShapeTool): @@ -1242,6 +1249,7 @@ class FCText(FCShapeTool): self.text_gui.text_path = [] self.text_gui.hide_tool() self.complete = True + self.draw_app.app.inform.emit("[success]Done. Adding Text completed.") def utility_geometry(self, data=None): """ @@ -1282,6 +1290,7 @@ class FCBuffer(FCShapeTool): self.draw_app.buffer(buffer_distance, join_style) self.app.ui.notebook.setTabText(2, "Tools") self.disactivate() + self.draw_app.app.inform.emit("[success]Done. Buffer Tool completed.") def on_buffer_int(self): buffer_distance = self.buff_tool.buffer_distance_entry.get_value() @@ -1291,6 +1300,7 @@ class FCBuffer(FCShapeTool): self.draw_app.buffer_int(buffer_distance, join_style) self.app.ui.notebook.setTabText(2, "Tools") self.disactivate() + self.draw_app.app.inform.emit("[success]Done. Buffer Int Tool completed.") def on_buffer_ext(self): buffer_distance = self.buff_tool.buffer_distance_entry.get_value() @@ -1300,6 +1310,7 @@ class FCBuffer(FCShapeTool): self.draw_app.buffer_ext(buffer_distance, join_style) self.app.ui.notebook.setTabText(2, "Tools") self.disactivate() + self.draw_app.app.inform.emit("[success]Done. Buffer Ext Tool completed.") def activate(self): self.buff_tool.buffer_button.clicked.disconnect() @@ -1361,6 +1372,7 @@ class FCRotate(FCShapeTool): # Delete old self.draw_app.delete_selected() self.complete = True + self.draw_app.app.inform.emit("[success]Done. Geometry rotate completed.") # MS: automatically select the Select Tool after finishing the action but is not working yet :( #self.draw_app.select_tool("select") @@ -2006,7 +2018,8 @@ class FlatCAMGeoEditor(QtCore.QObject): try: self.options[option] = float(entry.text()) except Exception as e: - log.debug(str(e)) + log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e)) + return self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator()) self.app.ui.grid_gap_x_entry.textChanged.connect( @@ -3020,7 +3033,13 @@ class FlatCAMGeoEditor(QtCore.QObject): shapes = self.get_selected() - results = shapes[0].geo + try: + results = shapes[0].geo + except Exception as e: + log.debug("FlatCAMGeoEditor.intersection() --> %s" % str(e)) + self.app.inform.emit("[warning_notcl]A selection of at least 2 geo items is required to do Intersection.") + self.select_tool('select') + return for shape in shapes[1:]: results = results.intersection(shape.geo) diff --git a/README.md b/README.md index 6c810bd1..287fdffc 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ CAD program, and create G-Code for Isolation routing. - fixed issue in Tool Calculators when a float value was entered starting only with the dot. - added protection for entering incorrect values in Offset and Scale fields for Gerber and Geometry objects (in Selected Tab) - added more shortcut keys in the Geometry Editor and in Excellon Editor; activated also the zoom (fit, in, out) shortcut keys ('1' , '2', '3') for the editors +- disabled the context menu in tools table on Paint Tool in case that the painting method is single. +- added protection when trying to do Intersection in Geometry Editor without having selected Geometry items. 28.01.2018 diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 4e6de871..1bf3551a 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -312,11 +312,13 @@ class ToolPaint(FlatCAMTool, Gerber): self.addtool_entry.setDisabled(True) self.addtool_btn.setDisabled(True) self.deltool_btn.setDisabled(True) + self.tools_table.setContextMenuPolicy(Qt.NoContextMenu) else: self.rest_cb.setDisabled(False) self.addtool_entry.setDisabled(False) self.addtool_btn.setDisabled(False) self.deltool_btn.setDisabled(False) + self.tools_table.setContextMenuPolicy(Qt.ActionsContextMenu) def set_ui(self): ## Init the GUI interface @@ -383,6 +385,10 @@ class ToolPaint(FlatCAMTool, Gerber): # through this, we add a initial row / tool in the tool_table self.on_tool_add(self.app.defaults["tools_painttooldia"], muted=True) + # if the Paint Method is "Single" disable the tool table context menu + if self.default_data["selectmethod"] == "single": + self.tools_table.setContextMenuPolicy(Qt.NoContextMenu) + def build_ui(self): try: From 5a0390f2a428d31a66313fe29d6f488609ba055f Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 15:56:49 +0200 Subject: [PATCH 19/23] - fixed the scale, mirror, rotate, skew functions to work with Geometry Objects of multi-geometry type. --- FlatCAMObj.py | 34 +++++++++++++++++++++++++--------- README.md | 2 ++ camlib.py | 20 +++++++++++++++++--- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 3c62f059..72d63280 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -3491,16 +3491,32 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): else: px, py = point - if type(self.solid_geometry) == list: - geo_list = self.flatten(self.solid_geometry) - self.solid_geometry = [] - # for g in geo_list: - # self.solid_geometry.append(affinity.scale(g, xfactor, yfactor, origin=(px, py))) - self.solid_geometry = [affinity.scale(g, xfactor, yfactor, origin=(px, py)) - for g in geo_list] + # if type(self.solid_geometry) == list: + # geo_list = self.flatten(self.solid_geometry) + # self.solid_geometry = [] + # # for g in geo_list: + # # self.solid_geometry.append(affinity.scale(g, xfactor, yfactor, origin=(px, py))) + # self.solid_geometry = [affinity.scale(g, xfactor, yfactor, origin=(px, py)) + # for g in geo_list] + # else: + # self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor, + # origin=(px, py)) + # self.app.inform.emit("[success]Geometry Scale done.") + + def scale_recursion(geom): + if type(geom) == list: + geoms=list() + for local_geom in geom: + geoms.append(scale_recursion(local_geom)) + return geoms + else: + return affinity.scale(geom, xfactor, yfactor, origin=(px, py)) + + if self.multigeo is True: + for tool in self.tools: + self.tools[tool]['solid_geometry'] = scale_recursion(self.tools[tool]['solid_geometry']) else: - self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor, - origin=(px, py)) + self.solid_geometry=scale_recursion(self.solid_geometry) self.app.inform.emit("[success]Geometry Scale done.") def offset(self, vect): diff --git a/README.md b/README.md index 287fdffc..3eeaf1d5 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ CAD program, and create G-Code for Isolation routing. - added more shortcut keys in the Geometry Editor and in Excellon Editor; activated also the zoom (fit, in, out) shortcut keys ('1' , '2', '3') for the editors - disabled the context menu in tools table on Paint Tool in case that the painting method is single. - added protection when trying to do Intersection in Geometry Editor without having selected Geometry items. +- fixed the scale, mirror, rotate, skew functions to work with Geometry Objects of multi-geometry type. + 28.01.2018 diff --git a/camlib.py b/camlib.py index ef317ee6..f544e487 100644 --- a/camlib.py +++ b/camlib.py @@ -1354,11 +1354,17 @@ class Geometry(object): return affinity.scale(obj, xscale, yscale, origin=(px,py)) try: - self.solid_geometry = mirror_geom(self.solid_geometry) + if self.multigeo is True: + for tool in self.tools: + self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry']) + else: + self.solid_geometry = mirror_geom(self.solid_geometry) self.app.inform.emit('[success]Object was mirrored ...') except AttributeError: self.app.inform.emit("[error_notcl] Failed to mirror. No object selected") + + def rotate(self, angle, point): """ Rotate an object by an angle (in degrees) around the provided coordinates. @@ -1388,7 +1394,11 @@ class Geometry(object): return affinity.rotate(obj, angle, origin=(px, py)) try: - self.solid_geometry = rotate_geom(self.solid_geometry) + if self.multigeo is True: + for tool in self.tools: + self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry']) + else: + self.solid_geometry = rotate_geom(self.solid_geometry) self.app.inform.emit('[success]Object was rotated ...') except AttributeError: self.app.inform.emit("[error_notcl] Failed to rotate. No object selected") @@ -1420,7 +1430,11 @@ class Geometry(object): return affinity.skew(obj, angle_x, angle_y, origin=(px, py)) try: - self.solid_geometry = skew_geom(self.solid_geometry) + if self.multigeo is True: + for tool in self.tools: + self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry']) + else: + self.solid_geometry = skew_geom(self.solid_geometry) self.app.inform.emit('[success]Object was skewed ...') except AttributeError: self.app.inform.emit("[error_notcl] Failed to skew. No object selected") From be2ecaa152cbe5e2f1ab108b1f56bceafad25ce4 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 21:47:27 +0200 Subject: [PATCH 20/23] - added a GUI for Excellon Search time for OR-TOOLS path optimization in Edit -> Preferences -> Excellon General -> Optimization Time - more changes in Edit -> Preferences -> Geometry, Gerber and in CNCJob - added new option for Cutout Tool Freeform Gaps in Edit -> Preferences -> Tools - fixed Freeform Cutout gaps issue (it was double than the value set) --- FlatCAMApp.py | 175 ++++++++++++----------- FlatCAMGUI.py | 282 +++++++++++++++++++++++++------------ README.md | 5 +- flatcamTools/ToolCutout.py | 20 ++- 4 files changed, 300 insertions(+), 182 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 265d1d71..92ffde68 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -313,20 +313,20 @@ class App(QtCore.QObject): "global_workspace": self.general_defaults_form.general_gui_group.workspace_cb, "global_workspaceT": self.general_defaults_form.general_gui_group.wk_cb, - "gerber_plot": self.gerber_defaults_form.gerber_group.plot_cb, - "gerber_solid": self.gerber_defaults_form.gerber_group.solid_cb, - "gerber_multicolored": self.gerber_defaults_form.gerber_group.multicolored_cb, - "gerber_isotooldia": self.gerber_defaults_form.gerber_group.iso_tool_dia_entry, - "gerber_isopasses": self.gerber_defaults_form.gerber_group.iso_width_entry, - "gerber_isooverlap": self.gerber_defaults_form.gerber_group.iso_overlap_entry, + "gerber_plot": self.gerber_defaults_form.gerber_gen_group.plot_cb, + "gerber_solid": self.gerber_defaults_form.gerber_gen_group.solid_cb, + "gerber_multicolored": self.gerber_defaults_form.gerber_gen_group.multicolored_cb, + "gerber_circle_steps": self.gerber_defaults_form.gerber_gen_group.circle_steps_entry, - "gerber_combine_passes": self.gerber_defaults_form.gerber_group.combine_passes_cb, - "gerber_milling_type": self.gerber_defaults_form.gerber_group.milling_type_radio, - "gerber_noncoppermargin": self.gerber_defaults_form.gerber_group.noncopper_margin_entry, - "gerber_noncopperrounded": self.gerber_defaults_form.gerber_group.noncopper_rounded_cb, - "gerber_bboxmargin": self.gerber_defaults_form.gerber_group.bbmargin_entry, - "gerber_bboxrounded": self.gerber_defaults_form.gerber_group.bbrounded_cb, - "gerber_circle_steps": self.gerber_defaults_form.gerber_group.circle_steps_entry, + "gerber_isotooldia": self.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, + "gerber_isopasses": self.gerber_defaults_form.gerber_opt_group.iso_width_entry, + "gerber_isooverlap": self.gerber_defaults_form.gerber_opt_group.iso_overlap_entry, + "gerber_combine_passes": self.gerber_defaults_form.gerber_opt_group.combine_passes_cb, + "gerber_milling_type": self.gerber_defaults_form.gerber_opt_group.milling_type_radio, + "gerber_noncoppermargin": self.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry, + "gerber_noncopperrounded": self.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb, + "gerber_bboxmargin": self.gerber_defaults_form.gerber_opt_group.bbmargin_entry, + "gerber_bboxrounded": self.gerber_defaults_form.gerber_opt_group.bbrounded_cb, "excellon_plot": self.excellon_defaults_form.excellon_gen_group.plot_cb, "excellon_solid": self.excellon_defaults_form.excellon_gen_group.solid_cb, @@ -337,6 +337,7 @@ class App(QtCore.QObject): "excellon_zeros": self.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio, "excellon_units": self.excellon_defaults_form.excellon_gen_group.excellon_units_radio, "excellon_optimization_type": self.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio, + "excellon_search_time": self.excellon_defaults_form.excellon_gen_group.optimization_time_entry, "excellon_drillz": self.excellon_defaults_form.excellon_opt_group.cutz_entry, "excellon_travelz": self.excellon_defaults_form.excellon_opt_group.travelz_entry, @@ -355,36 +356,38 @@ class App(QtCore.QObject): "excellon_slot_tooldia": self.excellon_defaults_form.excellon_opt_group.slot_tooldia_entry, "excellon_gcode_type": self.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio, - "geometry_plot": self.geometry_defaults_form.geometry_group.plot_cb, - "geometry_segx": self.geometry_defaults_form.geometry_group.segx_entry, - "geometry_segy": self.geometry_defaults_form.geometry_group.segy_entry, - "geometry_cutz": self.geometry_defaults_form.geometry_group.cutz_entry, - "geometry_travelz": self.geometry_defaults_form.geometry_group.travelz_entry, - "geometry_feedrate": self.geometry_defaults_form.geometry_group.cncfeedrate_entry, - "geometry_feedrate_z": self.geometry_defaults_form.geometry_group.cncplunge_entry, - "geometry_feedrate_rapid": self.geometry_defaults_form.geometry_group.cncfeedrate_rapid_entry, - "geometry_cnctooldia": self.geometry_defaults_form.geometry_group.cnctooldia_entry, - "geometry_spindlespeed": self.geometry_defaults_form.geometry_group.cncspindlespeed_entry, - "geometry_dwell": self.geometry_defaults_form.geometry_group.dwell_cb, - "geometry_dwelltime": self.geometry_defaults_form.geometry_group.dwelltime_entry, - "geometry_ppname_g": self.geometry_defaults_form.geometry_group.pp_geometry_name_cb, - "geometry_toolchange": self.geometry_defaults_form.geometry_group.toolchange_cb, - "geometry_toolchangez": self.geometry_defaults_form.geometry_group.toolchangez_entry, - "geometry_toolchangexy": self.geometry_defaults_form.geometry_group.toolchangexy_entry, - "geometry_startz": self.geometry_defaults_form.geometry_group.gstartz_entry, - "geometry_endz": self.geometry_defaults_form.geometry_group.gendz_entry, - "geometry_multidepth": self.geometry_defaults_form.geometry_group.multidepth_cb, - "geometry_depthperpass": self.geometry_defaults_form.geometry_group.depthperpass_entry, - "geometry_extracut": self.geometry_defaults_form.geometry_group.extracut_cb, - "geometry_circle_steps": self.geometry_defaults_form.geometry_group.circle_steps_entry, + "geometry_plot": self.geometry_defaults_form.geometry_gen_group.plot_cb, + "geometry_cnctooldia": self.geometry_defaults_form.geometry_gen_group.cnctooldia_entry, + "geometry_circle_steps": self.geometry_defaults_form.geometry_gen_group.circle_steps_entry, - "cncjob_plot": self.cncjob_defaults_form.cncjob_group.plot_cb, - "cncjob_tooldia": self.cncjob_defaults_form.cncjob_group.tooldia_entry, - "cncjob_coords_decimals": self.cncjob_defaults_form.cncjob_group.coords_dec_entry, - "cncjob_fr_decimals": self.cncjob_defaults_form.cncjob_group.fr_dec_entry, - "cncjob_prepend": self.cncjob_defaults_form.cncjob_group.prepend_text, - "cncjob_append": self.cncjob_defaults_form.cncjob_group.append_text, - "cncjob_steps_per_circle": self.cncjob_defaults_form.cncjob_group.steps_per_circle_entry, + "geometry_segx": self.geometry_defaults_form.geometry_opt_group.segx_entry, + "geometry_segy": self.geometry_defaults_form.geometry_opt_group.segy_entry, + "geometry_cutz": self.geometry_defaults_form.geometry_opt_group.cutz_entry, + "geometry_travelz": self.geometry_defaults_form.geometry_opt_group.travelz_entry, + "geometry_feedrate": self.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry, + "geometry_feedrate_z": self.geometry_defaults_form.geometry_opt_group.cncplunge_entry, + "geometry_feedrate_rapid": self.geometry_defaults_form.geometry_opt_group.cncfeedrate_rapid_entry, + "geometry_spindlespeed": self.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry, + "geometry_dwell": self.geometry_defaults_form.geometry_opt_group.dwell_cb, + "geometry_dwelltime": self.geometry_defaults_form.geometry_opt_group.dwelltime_entry, + "geometry_ppname_g": self.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb, + "geometry_toolchange": self.geometry_defaults_form.geometry_opt_group.toolchange_cb, + "geometry_toolchangez": self.geometry_defaults_form.geometry_opt_group.toolchangez_entry, + "geometry_toolchangexy": self.geometry_defaults_form.geometry_opt_group.toolchangexy_entry, + "geometry_startz": self.geometry_defaults_form.geometry_opt_group.gstartz_entry, + "geometry_endz": self.geometry_defaults_form.geometry_opt_group.gendz_entry, + "geometry_multidepth": self.geometry_defaults_form.geometry_opt_group.multidepth_cb, + "geometry_depthperpass": self.geometry_defaults_form.geometry_opt_group.depthperpass_entry, + "geometry_extracut": self.geometry_defaults_form.geometry_opt_group.extracut_cb, + + "cncjob_plot": self.cncjob_defaults_form.cncjob_gen_group.plot_cb, + "cncjob_tooldia": self.cncjob_defaults_form.cncjob_gen_group.tooldia_entry, + "cncjob_coords_decimals": self.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry, + "cncjob_fr_decimals": self.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry, + "cncjob_steps_per_circle": self.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry, + + "cncjob_prepend": self.cncjob_defaults_form.cncjob_opt_group.prepend_text, + "cncjob_append": self.cncjob_defaults_form.cncjob_opt_group.append_text, "tools_ncctools": self.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, "tools_nccoverlap": self.tools_defaults_form.tools_ncc_group.ncc_overlap_entry, @@ -398,6 +401,7 @@ class App(QtCore.QObject): "tools_cutoutmargin": self.tools_defaults_form.tools_cutout_group.cutout_margin_entry, "tools_cutoutgapsize": self.tools_defaults_form.tools_cutout_group.cutout_gap_entry, "tools_gaps_rect": self.tools_defaults_form.tools_cutout_group.gaps_radio, + "tools_gaps_ff": self.tools_defaults_form.tools_cutout_group.gaps_combo, "tools_painttooldia": self.tools_defaults_form.tools_paint_group.painttooldia_entry, "tools_paintoverlap": self.tools_defaults_form.tools_paint_group.paintoverlap_entry, @@ -411,7 +415,7 @@ class App(QtCore.QObject): self.postprocessors = load_postprocessors(self) for name in list(self.postprocessors.keys()): - self.geometry_defaults_form.geometry_group.pp_geometry_name_cb.addItem(name) + self.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb.addItem(name) # HPGL postprocessor is only for Geometry objects therefore it should not be in the Excellon Preferences if name == 'hpgl': continue @@ -558,6 +562,7 @@ class App(QtCore.QObject): "tools_cutoutmargin": 0.1, "tools_cutoutgapsize": 0.15, "tools_gaps_rect": "4", + "tools_gaps_ff": "8", "tools_painttooldia": 0.07, "tools_paintoverlap": 0.15, @@ -605,17 +610,19 @@ class App(QtCore.QObject): "units": self.general_options_form.general_app_group.units_radio, "global_gridx": self.general_options_form.general_gui_group.gridx_entry, "global_gridy": self.general_options_form.general_gui_group.gridy_entry, - "gerber_plot": self.gerber_options_form.gerber_group.plot_cb, - "gerber_solid": self.gerber_options_form.gerber_group.solid_cb, - "gerber_multicolored": self.gerber_options_form.gerber_group.multicolored_cb, - "gerber_isotooldia": self.gerber_options_form.gerber_group.iso_tool_dia_entry, - "gerber_isopasses": self.gerber_options_form.gerber_group.iso_width_entry, - "gerber_isooverlap": self.gerber_options_form.gerber_group.iso_overlap_entry, - "gerber_combine_passes": self.gerber_options_form.gerber_group.combine_passes_cb, - "gerber_noncoppermargin": self.gerber_options_form.gerber_group.noncopper_margin_entry, - "gerber_noncopperrounded": self.gerber_options_form.gerber_group.noncopper_rounded_cb, - "gerber_bboxmargin": self.gerber_options_form.gerber_group.bbmargin_entry, - "gerber_bboxrounded": self.gerber_options_form.gerber_group.bbrounded_cb, + + "gerber_plot": self.gerber_options_form.gerber_gen_group.plot_cb, + "gerber_solid": self.gerber_options_form.gerber_gen_group.solid_cb, + "gerber_multicolored": self.gerber_options_form.gerber_gen_group.multicolored_cb, + + "gerber_isotooldia": self.gerber_options_form.gerber_opt_group.iso_tool_dia_entry, + "gerber_isopasses": self.gerber_options_form.gerber_opt_group.iso_width_entry, + "gerber_isooverlap": self.gerber_options_form.gerber_opt_group.iso_overlap_entry, + "gerber_combine_passes": self.gerber_options_form.gerber_opt_group.combine_passes_cb, + "gerber_noncoppermargin": self.gerber_options_form.gerber_opt_group.noncopper_margin_entry, + "gerber_noncopperrounded": self.gerber_options_form.gerber_opt_group.noncopper_rounded_cb, + "gerber_bboxmargin": self.gerber_options_form.gerber_opt_group.bbmargin_entry, + "gerber_bboxrounded": self.gerber_options_form.gerber_opt_group.bbrounded_cb, "excellon_plot": self.excellon_options_form.excellon_gen_group.plot_cb, "excellon_solid": self.excellon_options_form.excellon_gen_group.solid_cb, @@ -642,32 +649,34 @@ class App(QtCore.QObject): "excellon_startz": self.excellon_options_form.excellon_opt_group.estartz_entry, "excellon_endz": self.excellon_options_form.excellon_opt_group.eendz_entry, - "geometry_plot": self.geometry_options_form.geometry_group.plot_cb, - "geometry_segx": self.geometry_options_form.geometry_group.segx_entry, - "geometry_segy": self.geometry_options_form.geometry_group.segy_entry, - "geometry_cutz": self.geometry_options_form.geometry_group.cutz_entry, - "geometry_travelz": self.geometry_options_form.geometry_group.travelz_entry, - "geometry_feedrate": self.geometry_options_form.geometry_group.cncfeedrate_entry, - "geometry_feedrate_z": self.geometry_options_form.geometry_group.cncplunge_entry, - "geometry_feedrate_rapid": self.geometry_options_form.geometry_group.cncfeedrate_rapid_entry, - "geometry_spindlespeed": self.geometry_options_form.geometry_group.cncspindlespeed_entry, - "geometry_dwell": self.geometry_options_form.geometry_group.dwell_cb, - "geometry_dwelltime": self.geometry_options_form.geometry_group.dwelltime_entry, - "geometry_cnctooldia": self.geometry_options_form.geometry_group.cnctooldia_entry, - "geometry_ppname_g": self.geometry_options_form.geometry_group.pp_geometry_name_cb, - "geometry_toolchange": self.geometry_options_form.geometry_group.toolchange_cb, - "geometry_toolchangez": self.geometry_options_form.geometry_group.toolchangez_entry, - "geometry_toolchangexy": self.geometry_options_form.geometry_group.toolchangexy_entry, - "geometry_startz": self.geometry_options_form.geometry_group.gstartz_entry, - "geometry_endz": self.geometry_options_form.geometry_group.gendz_entry, - "geometry_depthperpass": self.geometry_options_form.geometry_group.depthperpass_entry, - "geometry_multidepth": self.geometry_options_form.geometry_group.multidepth_cb, - "geometry_extracut": self.geometry_options_form.geometry_group.extracut_cb, + "geometry_plot": self.geometry_options_form.geometry_gen_group.plot_cb, + "geometry_cnctooldia": self.geometry_options_form.geometry_gen_group.cnctooldia_entry, - "cncjob_plot": self.cncjob_options_form.cncjob_group.plot_cb, - "cncjob_tooldia": self.cncjob_options_form.cncjob_group.tooldia_entry, - "cncjob_prepend": self.cncjob_options_form.cncjob_group.prepend_text, - "cncjob_append": self.cncjob_options_form.cncjob_group.append_text, + "geometry_segx": self.geometry_options_form.geometry_opt_group.segx_entry, + "geometry_segy": self.geometry_options_form.geometry_opt_group.segy_entry, + "geometry_cutz": self.geometry_options_form.geometry_opt_group.cutz_entry, + "geometry_travelz": self.geometry_options_form.geometry_opt_group.travelz_entry, + "geometry_feedrate": self.geometry_options_form.geometry_opt_group.cncfeedrate_entry, + "geometry_feedrate_z": self.geometry_options_form.geometry_opt_group.cncplunge_entry, + "geometry_feedrate_rapid": self.geometry_options_form.geometry_opt_group.cncfeedrate_rapid_entry, + "geometry_spindlespeed": self.geometry_options_form.geometry_opt_group.cncspindlespeed_entry, + "geometry_dwell": self.geometry_options_form.geometry_opt_group.dwell_cb, + "geometry_dwelltime": self.geometry_options_form.geometry_opt_group.dwelltime_entry, + "geometry_ppname_g": self.geometry_options_form.geometry_opt_group.pp_geometry_name_cb, + "geometry_toolchange": self.geometry_options_form.geometry_opt_group.toolchange_cb, + "geometry_toolchangez": self.geometry_options_form.geometry_opt_group.toolchangez_entry, + "geometry_toolchangexy": self.geometry_options_form.geometry_opt_group.toolchangexy_entry, + "geometry_startz": self.geometry_options_form.geometry_opt_group.gstartz_entry, + "geometry_endz": self.geometry_options_form.geometry_opt_group.gendz_entry, + "geometry_depthperpass": self.geometry_options_form.geometry_opt_group.depthperpass_entry, + "geometry_multidepth": self.geometry_options_form.geometry_opt_group.multidepth_cb, + "geometry_extracut": self.geometry_options_form.geometry_opt_group.extracut_cb, + + "cncjob_plot": self.cncjob_options_form.cncjob_gen_group.plot_cb, + "cncjob_tooldia": self.cncjob_options_form.cncjob_gen_group.tooldia_entry, + + "cncjob_prepend": self.cncjob_options_form.cncjob_opt_group.prepend_text, + "cncjob_append": self.cncjob_options_form.cncjob_opt_group.append_text, "tools_ncctools": self.tools_options_form.tools_ncc_group.ncc_tool_dia_entry, "tools_nccoverlap": self.tools_options_form.tools_ncc_group.ncc_overlap_entry, @@ -676,7 +685,8 @@ class App(QtCore.QObject): "tools_cutouttooldia": self.tools_options_form.tools_cutout_group.cutout_tooldia_entry, "tools_cutoutmargin": self.tools_options_form.tools_cutout_group.cutout_margin_entry, "tools_cutoutgapsize": self.tools_options_form.tools_cutout_group.cutout_gap_entry, - "tools_gaps": self.tools_options_form.tools_cutout_group.gaps_radio, + "tools_gaps_rect": self.tools_options_form.tools_cutout_group.gaps_radio, + "tools_gaps_rect": self.tools_options_form.tools_cutout_group.gaps_combo, "tools_painttooldia": self.tools_options_form.tools_paint_group.painttooldia_entry, "tools_paintoverlap": self.tools_options_form.tools_paint_group.paintoverlap_entry, @@ -688,7 +698,7 @@ class App(QtCore.QObject): } for name in list(self.postprocessors.keys()): - self.geometry_options_form.geometry_group.pp_geometry_name_cb.addItem(name) + self.geometry_options_form.geometry_opt_group.pp_geometry_name_cb.addItem(name) self.excellon_options_form.excellon_opt_group.pp_excellon_name_cb.addItem(name) self.options = LoudDict() @@ -773,7 +783,8 @@ class App(QtCore.QObject): "tools_cutouttooldia": 0.07, "tools_cutoutmargin": 0.1, "tools_cutoutgapsize": 0.15, - "tools_gaps": "4", + "tools_gaps_rect": "4", + "tools_gaps_ff": "8", "tools_painttooldia": 0.07, "tools_paintoverlap": 0.15, diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 2de7edf9..caf03ac2 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -916,12 +916,17 @@ class GerberPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent=parent) - self.layout = QtWidgets.QVBoxLayout() + self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) - self.gerber_group = GerberPrefGroupUI() - self.gerber_group.setFixedWidth(260) - self.layout.addWidget(self.gerber_group) + self.gerber_gen_group = GerberGenPrefGroupUI() + self.gerber_gen_group.setFixedWidth(260) + self.gerber_opt_group = GerberOptPrefGroupUI() + self.gerber_opt_group.setFixedWidth(260) + + self.layout.addWidget(self.gerber_gen_group) + self.layout.addWidget(self.gerber_opt_group) + self.layout.addStretch() class ExcellonPreferencesUI(QtWidgets.QWidget): @@ -945,12 +950,17 @@ class GeometryPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent=parent) - self.layout = QtWidgets.QVBoxLayout() + self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) - self.geometry_group = GeometryPrefGroupUI() - self.geometry_group.setFixedWidth(260) - self.layout.addWidget(self.geometry_group) + self.geometry_gen_group = GeometryGenPrefGroupUI() + self.geometry_gen_group.setFixedWidth(260) + self.geometry_opt_group = GeometryOptPrefGroupUI() + self.geometry_opt_group.setFixedWidth(260) + + self.layout.addWidget(self.geometry_gen_group) + self.layout.addWidget(self.geometry_opt_group) + self.layout.addStretch() class ToolsPreferencesUI(QtWidgets.QWidget): @@ -981,12 +991,17 @@ class CNCJobPreferencesUI(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent=parent) - self.layout = QtWidgets.QVBoxLayout() + self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) - self.cncjob_group = CNCJobPrefGroupUI() - self.cncjob_group.setFixedWidth(260) - self.layout.addWidget(self.cncjob_group) + self.cncjob_gen_group = CNCJobGenPrefGroupUI() + self.cncjob_gen_group.setFixedWidth(260) + self.cncjob_opt_group = CNCJobOptPrefGroupUI() + self.cncjob_opt_group.setFixedWidth(260) + + self.layout.addWidget(self.cncjob_gen_group) + self.layout.addWidget(self.cncjob_opt_group) + self.layout.addStretch() class OptionsGroupUI(QtWidgets.QGroupBox): @@ -1266,13 +1281,15 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): # Units for FlatCAM self.unitslabel = QtWidgets.QLabel('Units:') - self.unitslabel.setToolTip("Those are units in which FlatCAM works.") + self.unitslabel.setToolTip("The default value for FlatCAM units.\n" + "Whatever is selected here is set every time\n" + "FLatCAM is started.") self.units_radio = RadioSet([{'label': 'IN', 'value': 'IN'}, {'label': 'MM', 'value': 'MM'}]) # Languages for FlatCAM self.languagelabel = QtWidgets.QLabel('Languages:') - self.languagelabel.setToolTip("Set the language used for FlatCAM texts.") + self.languagelabel.setToolTip("Set the language used throughout FlatCAM.") self.language_cb = FCComboBox() self.languagespace = QtWidgets.QLabel('') self.language_apply_btn = FCButton("Apply Language") @@ -1317,9 +1334,11 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): # Select mouse pan button self.panbuttonlabel = QtWidgets.QLabel('Pan Button:') - self.panbuttonlabel.setToolTip("Select the mouse button to use for panning.") - self.pan_button_radio = RadioSet([{'label': 'Middle But.', 'value': '3'}, - {'label': 'Right But.', 'value': '2'}]) + self.panbuttonlabel.setToolTip("Select the mouse button to use for panning:\n" + "- MMB --> Middle Mouse Button\n" + "- RMB --> Middle Mouse Button") + self.pan_button_radio = RadioSet([{'label': 'MMB', 'value': '3'}, + {'label': 'RMB', 'value': '2'}]) # Multiple Selection Modifier Key self.mselectlabel = QtWidgets.QLabel('Multiple Sel:') @@ -1363,12 +1382,12 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): self.layout.addLayout(self.form_box) -class GerberPrefGroupUI(OptionsGroupUI): +class GerberGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): - # OptionsGroupUI.__init__(self, "Gerber Options", parent=parent) - super(GerberPrefGroupUI, self).__init__(self) + # OptionsGroupUI.__init__(self, "Gerber General Preferences", parent=parent) + super(GerberGenPrefGroupUI, self).__init__(self) - self.setTitle(str("Gerber Options")) + self.setTitle(str("Gerber General")) ## Plot options self.plot_options_label = QtWidgets.QLabel("Plot Options:") @@ -1407,6 +1426,17 @@ class GerberPrefGroupUI(OptionsGroupUI): self.circle_steps_entry = IntEntry() grid0.addWidget(self.circle_steps_entry, 1, 1) + self.layout.addStretch() + + +class GerberOptPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent) + super(GerberOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("Gerber Options")) + + ## Isolation Routing self.isolation_routing_label = QtWidgets.QLabel("Isolation Routing:") self.isolation_routing_label.setToolTip( @@ -1416,15 +1446,16 @@ class GerberPrefGroupUI(OptionsGroupUI): self.layout.addWidget(self.isolation_routing_label) # Cutting Tool Diameter - grid1 = QtWidgets.QGridLayout() - self.layout.addLayout(grid1) + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + tdlabel = QtWidgets.QLabel('Tool dia:') tdlabel.setToolTip( "Diameter of the cutting tool." ) - grid1.addWidget(tdlabel, 0, 0) + grid0.addWidget(tdlabel, 0, 0) self.iso_tool_dia_entry = LengthEntry() - grid1.addWidget(self.iso_tool_dia_entry, 0, 1) + grid0.addWidget(self.iso_tool_dia_entry, 0, 1) # Nr of passes passlabel = QtWidgets.QLabel('Width (# passes):') @@ -1432,9 +1463,9 @@ class GerberPrefGroupUI(OptionsGroupUI): "Width of the isolation gap in\n" "number (integer) of tool widths." ) - grid1.addWidget(passlabel, 1, 0) + grid0.addWidget(passlabel, 1, 0) self.iso_width_entry = IntEntry() - grid1.addWidget(self.iso_width_entry, 1, 1) + grid0.addWidget(self.iso_width_entry, 1, 1) # Pass overlap overlabel = QtWidgets.QLabel('Pass overlap:') @@ -1443,9 +1474,9 @@ class GerberPrefGroupUI(OptionsGroupUI): "Example:\n" "A value here of 0.25 means an overlap of 25% from the tool diameter found above." ) - grid1.addWidget(overlabel, 2, 0) + grid0.addWidget(overlabel, 2, 0) self.iso_overlap_entry = FloatEntry() - grid1.addWidget(self.iso_overlap_entry, 2, 1) + grid0.addWidget(self.iso_overlap_entry, 2, 1) milling_type_label = QtWidgets.QLabel('Milling Type:') milling_type_label.setToolTip( @@ -1453,17 +1484,17 @@ class GerberPrefGroupUI(OptionsGroupUI): "- climb / best for precision milling and to reduce tool usage\n" "- conventional / useful when there is no backlash compensation" ) - grid1.addWidget(milling_type_label, 3, 0) + grid0.addWidget(milling_type_label, 3, 0) self.milling_type_radio = RadioSet([{'label': 'Climb', 'value': 'cl'}, {'label': 'Conv.', 'value': 'cv'}]) - grid1.addWidget(self.milling_type_radio, 3, 1) + grid0.addWidget(self.milling_type_radio, 3, 1) # Combine passes self.combine_passes_cb = FCCheckBox(label='Combine Passes') self.combine_passes_cb.setToolTip( "Combine all passes into one object" ) - grid1.addWidget(self.combine_passes_cb, 4, 0) + grid0.addWidget(self.combine_passes_cb, 4, 0) ## Clear non-copper regions self.clearcopper_label = QtWidgets.QLabel("Clear non-copper:") @@ -1473,8 +1504,8 @@ class GerberPrefGroupUI(OptionsGroupUI): ) self.layout.addWidget(self.clearcopper_label) - grid3 = QtWidgets.QGridLayout() - self.layout.addLayout(grid3) + grid1 = QtWidgets.QGridLayout() + self.layout.addLayout(grid1) # Margin bmlabel = QtWidgets.QLabel('Boundary Margin:') @@ -1484,9 +1515,9 @@ class GerberPrefGroupUI(OptionsGroupUI): "objects with this minimum\n" "distance." ) - grid3.addWidget(bmlabel, 0, 0) + grid1.addWidget(bmlabel, 0, 0) self.noncopper_margin_entry = LengthEntry() - grid3.addWidget(self.noncopper_margin_entry, 0, 1) + grid1.addWidget(self.noncopper_margin_entry, 0, 1) # Rounded corners self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners") @@ -1494,23 +1525,23 @@ class GerberPrefGroupUI(OptionsGroupUI): "Creates a Geometry objects with polygons\n" "covering the copper-free areas of the PCB." ) - grid3.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2) + grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2) ## Bounding box self.boundingbox_label = QtWidgets.QLabel('Bounding Box:') self.layout.addWidget(self.boundingbox_label) - grid4 = QtWidgets.QGridLayout() - self.layout.addLayout(grid4) + grid2 = QtWidgets.QGridLayout() + self.layout.addLayout(grid2) bbmargin = QtWidgets.QLabel('Boundary Margin:') bbmargin.setToolTip( "Distance of the edges of the box\n" "to the nearest polygon." ) - grid4.addWidget(bbmargin, 0, 0) + grid2.addWidget(bbmargin, 0, 0) self.bbmargin_entry = LengthEntry() - grid4.addWidget(self.bbmargin_entry, 0, 1) + grid2.addWidget(self.bbmargin_entry, 0, 1) self.bbrounded_cb = FCCheckBox(label="Rounded corners") self.bbrounded_cb.setToolTip( @@ -1519,7 +1550,7 @@ class GerberPrefGroupUI(OptionsGroupUI): "their radius is equal to\n" "the margin." ) - grid4.addWidget(self.bbrounded_cb, 1, 0, 1, 2) + grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2) self.layout.addStretch() @@ -1738,16 +1769,41 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): form_box_excellon.addRow(self.excellon_optimization_label, self.excellon_optimization_radio) + self.optimization_time_label = QtWidgets.QLabel('Optimization Time: ') + self.optimization_time_label.setAlignment(QtCore.Qt.AlignLeft) + self.optimization_time_label.setToolTip( + "When OR-Tools Metaheuristic (MH) is enabled there is a\n" + "maximum threshold for how much time is spent doing the\n" + "path optimization. This max duration is set here." + + ) + + self.optimization_time_entry = LengthEntry() + form_box_excellon.addRow(self.optimization_time_label, self.optimization_time_entry) + current_platform = platform.architecture()[0] if current_platform == '64bit': self.excellon_optimization_label.setDisabled(False) self.excellon_optimization_radio.setDisabled(False) + self.optimization_time_label.setDisabled(False) + self.optimization_time_entry.setDisabled(False) + self.excellon_optimization_radio.activated_custom.connect(self.optimization_selection) + else: self.excellon_optimization_label.setDisabled(True) self.excellon_optimization_radio.setDisabled(True) + self.optimization_time_label.setDisabled(True) + self.optimization_time_entry.setDisabled(True) self.layout.addStretch() + def optimization_selection(self): + if self.excellon_optimization_radio.get_value() == 'M': + self.optimization_time_label.setDisabled(False) + self.optimization_time_entry.setDisabled(False) + else: + self.optimization_time_label.setDisabled(True) + self.optimization_time_entry.setDisabled(True) class ExcellonOptPrefGroupUI(OptionsGroupUI): @@ -1944,12 +2000,12 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): self.layout.addStretch() -class GeometryPrefGroupUI(OptionsGroupUI): +class GeometryGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): - # OptionsGroupUI.__init__(self, "Geometry Options", parent=parent) - super(GeometryPrefGroupUI, self).__init__(self) + # OptionsGroupUI.__init__(self, "Geometry General Preferences", parent=parent) + super(GeometryGenPrefGroupUI, self).__init__(self) - self.setTitle(str("Geometry Options")) + self.setTitle(str("Geometry General")) ## Plot options self.plot_options_label = QtWidgets.QLabel("Plot Options:") @@ -1991,6 +2047,16 @@ class GeometryPrefGroupUI(OptionsGroupUI): self.cnctooldia_entry = LengthEntry() grid1.addWidget(self.cnctooldia_entry, 0, 1) + self.layout.addStretch() + + +class GeometryOptPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Geometry Options Preferences", parent=parent) + super(GeometryOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("Geometry Options")) + # ------------------------------ ## Create CNC Job # ------------------------------ @@ -2002,8 +2068,8 @@ class GeometryPrefGroupUI(OptionsGroupUI): ) self.layout.addWidget(self.cncjob_label) - grid2 = QtWidgets.QGridLayout() - self.layout.addLayout(grid2) + grid1 = QtWidgets.QGridLayout() + self.layout.addLayout(grid1) # Cut Z cutzlabel = QtWidgets.QLabel('Cut Z:') @@ -2011,16 +2077,16 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Cutting depth (negative)\n" "below the copper surface." ) - grid2.addWidget(cutzlabel, 0, 0) + grid1.addWidget(cutzlabel, 0, 0) self.cutz_entry = LengthEntry() - grid2.addWidget(self.cutz_entry, 0, 1) + grid1.addWidget(self.cutz_entry, 0, 1) # Multidepth CheckBox self.multidepth_cb = FCCheckBox(label='Multidepth') self.multidepth_cb.setToolTip( "Multidepth usage: True or False." ) - grid2.addWidget(self.multidepth_cb, 1, 0) + grid1.addWidget(self.multidepth_cb, 1, 0) # Depth/pass dplabel = QtWidgets.QLabel('Depth/Pass:') @@ -2032,9 +2098,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "which has negative value." ) - grid2.addWidget(dplabel, 2, 0) + grid1.addWidget(dplabel, 2, 0) self.depthperpass_entry = LengthEntry() - grid2.addWidget(self.depthperpass_entry, 2, 1) + grid1.addWidget(self.depthperpass_entry, 2, 1) self.ois_multidepth = OptionalInputSection(self.multidepth_cb, [self.depthperpass_entry]) @@ -2044,9 +2110,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Height of the tool when\n" "moving without cutting." ) - grid2.addWidget(travelzlabel, 3, 0) + grid1.addWidget(travelzlabel, 3, 0) self.travelz_entry = LengthEntry() - grid2.addWidget(self.travelz_entry, 3, 1) + grid1.addWidget(self.travelz_entry, 3, 1) # Tool change: toolchlabel = QtWidgets.QLabel("Tool change:") @@ -2055,26 +2121,26 @@ class GeometryPrefGroupUI(OptionsGroupUI): "in G-Code (Pause for tool change)." ) self.toolchange_cb = FCCheckBox() - grid2.addWidget(toolchlabel, 4, 0) - grid2.addWidget(self.toolchange_cb, 4, 1) + grid1.addWidget(toolchlabel, 4, 0) + grid1.addWidget(self.toolchange_cb, 4, 1) # Toolchange Z toolchangezlabel = QtWidgets.QLabel('Toolchange Z:') toolchangezlabel.setToolTip( "Toolchange Z position." ) - grid2.addWidget(toolchangezlabel, 5, 0) + grid1.addWidget(toolchangezlabel, 5, 0) self.toolchangez_entry = LengthEntry() - grid2.addWidget(self.toolchangez_entry, 5, 1) + grid1.addWidget(self.toolchangez_entry, 5, 1) # Toolchange X,Y toolchange_xy_label = QtWidgets.QLabel('Toolchange X,Y:') toolchange_xy_label.setToolTip( "Toolchange X,Y position." ) - grid2.addWidget(toolchange_xy_label, 6, 0) + grid1.addWidget(toolchange_xy_label, 6, 0) self.toolchangexy_entry = FCEntry() - grid2.addWidget(self.toolchangexy_entry, 6, 1) + grid1.addWidget(self.toolchangexy_entry, 6, 1) # Start move Z startzlabel = QtWidgets.QLabel('Start move Z:') @@ -2082,9 +2148,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Height of the tool just after starting the work.\n" "Delete the value if you don't need this feature." ) - grid2.addWidget(startzlabel, 7, 0) + grid1.addWidget(startzlabel, 7, 0) self.gstartz_entry = FloatEntry() - grid2.addWidget(self.gstartz_entry, 7, 1) + grid1.addWidget(self.gstartz_entry, 7, 1) # End move Z endzlabel = QtWidgets.QLabel('End move Z:') @@ -2092,9 +2158,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Height of the tool after\n" "the last move at the end of the job." ) - grid2.addWidget(endzlabel, 8, 0) + grid1.addWidget(endzlabel, 8, 0) self.gendz_entry = LengthEntry() - grid2.addWidget(self.gendz_entry, 8, 1) + grid1.addWidget(self.gendz_entry, 8, 1) # Feedrate X-Y frlabel = QtWidgets.QLabel('Feed Rate X-Y:') @@ -2102,9 +2168,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Cutting speed in the XY\n" "plane in units per minute" ) - grid2.addWidget(frlabel, 9, 0) + grid1.addWidget(frlabel, 9, 0) self.cncfeedrate_entry = LengthEntry() - grid2.addWidget(self.cncfeedrate_entry, 9, 1) + grid1.addWidget(self.cncfeedrate_entry, 9, 1) # Feedrate Z (Plunge) frz_label = QtWidgets.QLabel('Feed Rate Z:') @@ -2113,9 +2179,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "plane in units per minute.\n" "It is called also Plunge." ) - grid2.addWidget(frz_label, 10, 0) + grid1.addWidget(frz_label, 10, 0) self.cncplunge_entry = LengthEntry() - grid2.addWidget(self.cncplunge_entry, 10, 1) + grid1.addWidget(self.cncplunge_entry, 10, 1) # Feedrate rapids fr_rapid_label = QtWidgets.QLabel('Feed Rate Rapids:') @@ -2123,9 +2189,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Cutting speed in the XY\n" "plane in units per minute" ) - grid2.addWidget(fr_rapid_label, 11, 0) + grid1.addWidget(fr_rapid_label, 11, 0) self.cncfeedrate_rapid_entry = LengthEntry() - grid2.addWidget(self.cncfeedrate_rapid_entry, 11, 1) + grid1.addWidget(self.cncfeedrate_rapid_entry, 11, 1) # End move extra cut self.extracut_cb = FCCheckBox(label='Cut over 1st pt.') @@ -2135,7 +2201,7 @@ class GeometryPrefGroupUI(OptionsGroupUI): "meet with last cut, we generate an\n" "extended cut over the first cut section." ) - grid2.addWidget(self.extracut_cb, 12, 0) + grid1.addWidget(self.extracut_cb, 12, 0) # Spindle Speed spdlabel = QtWidgets.QLabel('Spindle speed:') @@ -2143,9 +2209,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Speed of the spindle\n" "in RPM (optional)" ) - grid2.addWidget(spdlabel, 13, 0) + grid1.addWidget(spdlabel, 13, 0) self.cncspindlespeed_entry = IntEntry(allow_empty=True) - grid2.addWidget(self.cncspindlespeed_entry, 13, 1) + grid1.addWidget(self.cncspindlespeed_entry, 13, 1) # Dwell self.dwell_cb = FCCheckBox(label='Dwell:') @@ -2158,9 +2224,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Number of milliseconds for spindle to dwell." ) self.dwelltime_entry = FCEntry() - grid2.addWidget(self.dwell_cb, 14, 0) - grid2.addWidget(dwelltime, 15, 0) - grid2.addWidget(self.dwelltime_entry, 15, 1) + grid1.addWidget(self.dwell_cb, 14, 0) + grid1.addWidget(dwelltime, 15, 0) + grid1.addWidget(self.dwelltime_entry, 15, 1) self.ois_dwell = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry]) @@ -2170,10 +2236,10 @@ class GeometryPrefGroupUI(OptionsGroupUI): "The postprocessor file that dictates\n" "Machine Code output." ) - grid2.addWidget(pp_label, 16, 0) + grid1.addWidget(pp_label, 16, 0) self.pp_geometry_name_cb = FCComboBox() self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus) - grid2.addWidget(self.pp_geometry_name_cb, 16, 1) + grid1.addWidget(self.pp_geometry_name_cb, 16, 1) # Size of trace segment on X axis segx_label = QtWidgets.QLabel("Seg. X size:") @@ -2182,9 +2248,9 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Useful for auto-leveling.\n" "A value of 0 means no segmentation on the X axis." ) - grid2.addWidget(segx_label, 17, 0) + grid1.addWidget(segx_label, 17, 0) self.segx_entry = FCEntry() - grid2.addWidget(self.segx_entry, 17, 1) + grid1.addWidget(self.segx_entry, 17, 1) # Size of trace segment on Y axis segy_label = QtWidgets.QLabel("Seg. Y size:") @@ -2193,19 +2259,19 @@ class GeometryPrefGroupUI(OptionsGroupUI): "Useful for auto-leveling.\n" "A value of 0 means no segmentation on the Y axis." ) - grid2.addWidget(segy_label, 18, 0) + grid1.addWidget(segy_label, 18, 0) self.segy_entry = FCEntry() - grid2.addWidget(self.segy_entry, 18, 1) + grid1.addWidget(self.segy_entry, 18, 1) self.layout.addStretch() -class CNCJobPrefGroupUI(OptionsGroupUI): +class CNCJobGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): - # OptionsGroupUI.__init__(self, "CNC Job Options", parent=None) - super(CNCJobPrefGroupUI, self).__init__(self) + # OptionsGroupUI.__init__(self, "CNC Job General Preferences", parent=None) + super(CNCJobGenPrefGroupUI, self).__init__(self) - self.setTitle(str("CNC Job Options")) + self.setTitle(str("CNC Job General")) ## Plot options self.plot_options_label = QtWidgets.QLabel("Plot Options:") @@ -2262,6 +2328,16 @@ class CNCJobPrefGroupUI(OptionsGroupUI): self.fr_dec_entry = IntEntry() grid0.addWidget(self.fr_dec_entry, 4, 1) + self.layout.addStretch() + + +class CNCJobOptPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "CNC Job Options Preferences", parent=None) + super(CNCJobOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("CNC Job Options")) + ## Export G-Code self.export_gcode_label = QtWidgets.QLabel("Export G-Code:") self.export_gcode_label.setToolTip( @@ -2293,8 +2369,7 @@ class CNCJobPrefGroupUI(OptionsGroupUI): self.append_text = FCTextArea() self.layout.addWidget(self.append_text) - grid0 = QtWidgets.QGridLayout() - self.layout.addLayout(grid0) + self.layout.addStretch() class ToolsNCCPrefGroupUI(OptionsGroupUI): @@ -2444,10 +2519,12 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): self.cutout_gap_entry = LengthEntry() grid0.addWidget(self.cutout_gap_entry, 2, 1) - gapslabel = QtWidgets.QLabel('Gaps:') + gapslabel = QtWidgets.QLabel('Gaps Rect:') gapslabel.setToolTip( - "Where to place the gaps, Top/Bottom\n" - "Left/Rigt, or on all 4 sides." + "Where to place the gaps when doing a Rectangular Cutout:\n" + " - 2 (T/B) --> Top/Bottom\n" + " - 2 (L/R) --> Left/Rigt\n" + " - 4 --> on each of all 4 sides." ) grid0.addWidget(gapslabel, 3, 0) self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'}, @@ -2455,6 +2532,27 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): {'label': '4', 'value': '4'}]) grid0.addWidget(self.gaps_radio, 3, 1) + gaps_ff_label = QtWidgets.QLabel('Gaps FF:') + gaps_ff_label.setToolTip( + "Number of gaps used for the FreeForm cutout.\n" + "There can be maximum 8 bridges/gaps.\n" + "The choices are:\n" + "- lr - left + right\n" + "- tb - top + bottom\n" + "- 4 - left + right +top + bottom\n" + "- 2lr - 2*left + 2*right\n" + "- 2tb - 2*top + 2*bottom\n" + "- 8 - 2*left + 2*right +2*top + 2*bottom" + ) + grid0.addWidget(gaps_ff_label, 4, 0) + self.gaps_combo = FCComboBox() + grid0.addWidget(self.gaps_combo, 4, 1) + + gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8'] + for it in gaps_items: + self.gaps_combo.addItem(it) + self.gaps_combo.setStyleSheet('background-color: rgb(255,255,255)') + self.layout.addStretch() diff --git a/README.md b/README.md index 3eeaf1d5..7c8f6e48 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,10 @@ CAD program, and create G-Code for Isolation routing. - disabled the context menu in tools table on Paint Tool in case that the painting method is single. - added protection when trying to do Intersection in Geometry Editor without having selected Geometry items. - fixed the scale, mirror, rotate, skew functions to work with Geometry Objects of multi-geometry type. - +- added a GUI for Excellon Search time for OR-TOOLS path optimization in Edit -> Preferences -> Excellon General -> Optimization Time +- more changes in Edit -> Preferences -> Geometry, Gerber and in CNCJob +- added new option for Cutout Tool Freeform Gaps in Edit -> Preferences -> Tools +- fixed Freeform Cutout gaps issue (it was double than the value set) 28.01.2018 diff --git a/flatcamTools/ToolCutout.py b/flatcamTools/ToolCutout.py index 3a24a54c..0926d969 100644 --- a/flatcamTools/ToolCutout.py +++ b/flatcamTools/ToolCutout.py @@ -101,10 +101,9 @@ class ToolCutout(FlatCAMTool): # 8 - 2*left + 2*right +2*top + 2*bottom # Gaps - self.gaps = FCEntry() - self.gaps_label = QtWidgets.QLabel("Type of gaps: ") - self.gaps_label.setToolTip( - "Number of gaps used for the cutout.\n" + gaps_ff_label = QtWidgets.QLabel('Gaps FF: ') + gaps_ff_label.setToolTip( + "Number of gaps used for the FreeForm cutout.\n" "There can be maximum 8 bridges/gaps.\n" "The choices are:\n" "- lr - left + right\n" @@ -114,7 +113,13 @@ class ToolCutout(FlatCAMTool): "- 2tb - 2*top + 2*bottom\n" "- 8 - 2*left + 2*right +2*top + 2*bottom" ) - form_layout_2.addRow(self.gaps_label, self.gaps) + + self.gaps = FCComboBox() + gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8'] + for it in gaps_items: + self.gaps.addItem(it) + self.gaps.setStyleSheet('background-color: rgb(255,255,255)') + form_layout_2.addRow(gaps_ff_label, self.gaps) ## Buttons hlay = QtWidgets.QHBoxLayout() @@ -215,7 +220,8 @@ class ToolCutout(FlatCAMTool): return "Could not retrieve object: %s" % name if cutout_obj is None: - self.app.inform.emit("[error_notcl]Object not found: %s" % cutout_obj) + self.app.inform.emit("[error_notcl]There is no object selected for Cutout.\nSelect one and try again.") + return try: dia = float(self.dia.get_value()) @@ -254,7 +260,7 @@ class ToolCutout(FlatCAMTool): lenghtx = (xmax - xmin) + (margin * 2) lenghty = (ymax - ymin) + (margin * 2) - gapsize = gapsize + (dia / 2) + gapsize = gapsize / 2 + (dia / 2) if isinstance(cutout_obj,FlatCAMGeometry): # rename the obj name so it can be identified as cutout From e3d51c9da5576f0fe92983e1659300c519d34178 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 22:03:24 +0200 Subject: [PATCH 21/23] - added protection so the Cutout (either Freeform or Rectangular) cannot be done on a multigeo Geometry --- README.md | 1 + flatcamTools/ToolCutout.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/README.md b/README.md index 7c8f6e48..deee786d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ CAD program, and create G-Code for Isolation routing. - more changes in Edit -> Preferences -> Geometry, Gerber and in CNCJob - added new option for Cutout Tool Freeform Gaps in Edit -> Preferences -> Tools - fixed Freeform Cutout gaps issue (it was double than the value set) +- added protection so the Cutout (either Freeform or Rectangular) cannot be done on a multigeo Geometry 28.01.2018 diff --git a/flatcamTools/ToolCutout.py b/flatcamTools/ToolCutout.py index 0926d969..4d2e7dbd 100644 --- a/flatcamTools/ToolCutout.py +++ b/flatcamTools/ToolCutout.py @@ -253,6 +253,12 @@ class ToolCutout(FlatCAMTool): "Fill in a correct value and retry. ") return + if cutout_obj.multigeo is True: + self.app.inform.emit("[error]Cutout operation cannot be done on a multi-geo Geometry.\n" + "Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n" + "and after that perform Cutout.") + return + # Get min and max data for each object as we just cut rectangles across X or Y xmin, ymin, xmax, ymax = cutout_obj.bounds() px = 0.5 * (xmin + xmax) + margin @@ -363,6 +369,12 @@ class ToolCutout(FlatCAMTool): self.app.inform.emit("[error_notcl]Tool Diameter is zero value. Change it to a positive integer.") return "Tool Diameter is zero value. Change it to a positive integer." + if cutout_obj.multigeo is True: + self.app.inform.emit("[error]Cutout operation cannot be done on a multi-geo Geometry.\n" + "Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n" + "and after that perform Cutout.") + return + def geo_init(geo_obj, app_obj): real_margin = margin + (dia / 2) real_gap_size = gapsize + dia From 369d9256daf473bac90738ac71aac791182925e2 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 22:33:04 +0200 Subject: [PATCH 22/23] - added 2Sided Tool default values in Edit -> Preferences -> Tools --- FlatCAMApp.py | 26 +++++++++++--- FlatCAMGUI.py | 68 ++++++++++++++++++++++++++++++++++-- README.md | 1 + flatcamTools/ToolDblSided.py | 33 +++++++++-------- 4 files changed, 107 insertions(+), 21 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 92ffde68..49420f69 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -409,7 +409,11 @@ class App(QtCore.QObject): "tools_paintmethod": self.tools_defaults_form.tools_paint_group.paintmethod_combo, "tools_selectmethod": self.tools_defaults_form.tools_paint_group.selectmethod_combo, "tools_pathconnect": self.tools_defaults_form.tools_paint_group.pathconnect_cb, - "tools_paintcontour": self.tools_defaults_form.tools_paint_group.contour_cb + "tools_paintcontour": self.tools_defaults_form.tools_paint_group.contour_cb, + + "tools_2sided_mirror_axis": self.tools_defaults_form.tools_2sided_group.mirror_axis_radio, + "tools_2sided_axis_loc": self.tools_defaults_form.tools_2sided_group.axis_location_radio, + "tools_2sided_drilldia": self.tools_defaults_form.tools_2sided_group.drill_dia_entry, } # loads postprocessors self.postprocessors = load_postprocessors(self) @@ -570,7 +574,11 @@ class App(QtCore.QObject): "tools_paintmethod": "seed", "tools_selectmethod": "single", "tools_pathconnect": True, - "tools_paintcontour": True + "tools_paintcontour": True, + + "tools_2sided_mirror_axis": "X", + "tools_2sided_axis_loc": "point", + "tools_2sided_drilldia": 1, }) @@ -686,7 +694,7 @@ class App(QtCore.QObject): "tools_cutoutmargin": self.tools_options_form.tools_cutout_group.cutout_margin_entry, "tools_cutoutgapsize": self.tools_options_form.tools_cutout_group.cutout_gap_entry, "tools_gaps_rect": self.tools_options_form.tools_cutout_group.gaps_radio, - "tools_gaps_rect": self.tools_options_form.tools_cutout_group.gaps_combo, + "tools_gaps_ff": self.tools_options_form.tools_cutout_group.gaps_combo, "tools_painttooldia": self.tools_options_form.tools_paint_group.painttooldia_entry, "tools_paintoverlap": self.tools_options_form.tools_paint_group.paintoverlap_entry, @@ -694,7 +702,11 @@ class App(QtCore.QObject): "tools_paintmethod": self.tools_options_form.tools_paint_group.paintmethod_combo, "tools_selectmethod": self.tools_options_form.tools_paint_group.selectmethod_combo, "tools_pathconnect": self.tools_options_form.tools_paint_group.pathconnect_cb, - "tools_paintcontour": self.tools_options_form.tools_paint_group.contour_cb + "tools_paintcontour": self.tools_options_form.tools_paint_group.contour_cb, + + "tools_2sided_mirror_axis": self.tools_options_form.tools_2sided_group.mirror_axis_radio, + "tools_2sided_axis_loc": self.tools_options_form.tools_2sided_group.axis_location_radio, + "tools_2sided_drilldia": self.tools_options_form.tools_2sided_group.drill_dia_entry } for name in list(self.postprocessors.keys()): @@ -792,7 +804,11 @@ class App(QtCore.QObject): "tools_paintmethod": "seed", "tools_selectmethod": "single", "tools_pathconnect": True, - "tools_paintcontour": True + "tools_paintcontour": True, + + "tools_2sided_mirror_axis": "X", + "tools_2sided_axis_loc": 'point', + "tools_2sided_drilldia": 1 }) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index caf03ac2..edd31a4d 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -978,13 +978,20 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.tools_cutout_group = ToolsCutoutPrefGroupUI() self.tools_cutout_group.setFixedWidth(260) + self.tools_2sided_group = Tools2sidedPrefGroupUI() + self.tools_2sided_group.setFixedWidth(260) + self.vlay = QtWidgets.QVBoxLayout() self.vlay.addWidget(self.tools_ncc_group) self.vlay.addWidget(self.tools_paint_group) - self.layout.addLayout(self.vlay) + self.vlay1 = QtWidgets.QVBoxLayout() + self.vlay1.addWidget(self.tools_cutout_group) + self.vlay1.addWidget(self.tools_2sided_group) + + self.layout.addLayout(self.vlay) + self.layout.addLayout(self.vlay1) - self.layout.addWidget(self.tools_cutout_group) self.layout.addStretch() class CNCJobPreferencesUI(QtWidgets.QWidget): @@ -2556,6 +2563,63 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class Tools2sidedPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "2sided Tool Options", parent=parent) + super(Tools2sidedPrefGroupUI, self).__init__(self) + + self.setTitle(str("2Sided Tool Options")) + + ## Board cuttout + self.dblsided_label = QtWidgets.QLabel("Double Sided:") + self.dblsided_label.setToolTip( + "A tool to help in creating a double sided\n" + "PCB using alignment holes." + ) + self.layout.addWidget(self.dblsided_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + ## Drill diameter for alignment holes + self.drill_dia_entry = LengthEntry() + self.dd_label = QtWidgets.QLabel("Drill diam.:") + self.dd_label.setToolTip( + "Diameter of the drill for the " + "alignment holes." + ) + grid0.addWidget(self.dd_label, 0, 0) + grid0.addWidget(self.drill_dia_entry, 0, 1) + + ## Axis + self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'}, + {'label': 'Y', 'value': 'Y'}]) + self.mirax_label = QtWidgets.QLabel("Mirror Axis:") + self.mirax_label.setToolTip( + "Mirror vertically (X) or horizontally (Y)." + ) + # grid_lay.addRow("Mirror Axis:", self.mirror_axis) + self.empty_lb1 = QtWidgets.QLabel("") + grid0.addWidget(self.empty_lb1, 1, 0) + grid0.addWidget(self.mirax_label, 2, 0) + grid0.addWidget(self.mirror_axis_radio, 2, 1) + + ## Axis Location + self.axis_location_radio = RadioSet([{'label': 'Point', 'value': 'point'}, + {'label': 'Box', 'value': 'box'}]) + self.axloc_label = QtWidgets.QLabel("Axis Ref:") + self.axloc_label.setToolTip( + "The axis should pass through a point or cut\n " + "a specified box (in a Geometry object) in \n" + "the middle." + ) + # grid_lay.addRow("Axis Location:", self.axis_location) + grid0.addWidget(self.axloc_label, 3, 0) + grid0.addWidget(self.axis_location_radio, 3, 1) + + self.layout.addStretch() + + class ToolsPaintPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): # OptionsGroupUI.__init__(self, "Paint Area Tool Options", parent=parent) diff --git a/README.md b/README.md index deee786d..487303f1 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ CAD program, and create G-Code for Isolation routing. - added new option for Cutout Tool Freeform Gaps in Edit -> Preferences -> Tools - fixed Freeform Cutout gaps issue (it was double than the value set) - added protection so the Cutout (either Freeform or Rectangular) cannot be done on a multigeo Geometry +- added 2Sided Tool default values in Edit -> Preferences -> Tools 28.01.2018 diff --git a/flatcamTools/ToolDblSided.py b/flatcamTools/ToolDblSided.py index 783b14a1..475c3db3 100644 --- a/flatcamTools/ToolDblSided.py +++ b/flatcamTools/ToolDblSided.py @@ -249,14 +249,27 @@ class DblSidedTool(FlatCAMTool): self.drill_values = "" - ## Initialize form - self.mirror_axis.set_value('X') - self.axis_location.set_value('point') - self.drill_dia.set_value(1) + self.set_ui() def install(self, icon=None, separator=None, **kwargs): FlatCAMTool.install(self, icon, separator, shortcut='ALT+D', **kwargs) + def run(self): + FlatCAMTool.run(self) + + self.app.ui.notebook.setTabText(2, "2-Sided Tool") + self.reset_fields() + self.set_ui() + + def set_ui(self): + ## Initialize form + self.point_entry.set_value("") + self.alignment_holes.set_value("") + + self.mirror_axis.set_value(self.app.defaults["tools_2sided_mirror_axis"]) + self.axis_location.set_value(self.app.defaults["tools_2sided_axis_loc"]) + self.drill_dia.set_value(self.app.defaults["tools_2sided_drilldia"]) + def on_combo_box_type(self): obj_type = self.box_combo_type.currentIndex() self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) @@ -457,14 +470,6 @@ class DblSidedTool(FlatCAMTool): self.drill_values = "" - self.point_entry.set_value("") - self.alignment_holes.set_value("") - ## Initialize form - self.mirror_axis.set_value('X') - self.axis_location.set_value('point') - self.drill_dia.set_value(1) - def run(self): - FlatCAMTool.run(self) - self.app.ui.notebook.setTabText(2, "2-Sided Tool") - self.reset_fields() + + From 97c5c3fe1c3a759bdd481544e80d022aa74ef95b Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 29 Jan 2019 23:01:41 +0200 Subject: [PATCH 23/23] - optimized the FlatCAMCNCJob.on_plot_cb_click_table() plot function and solved a bug regarding having tools numbers not in sync with the cnc tool table --- FlatCAMObj.py | 7 +++++-- README.md | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 72d63280..8431132e 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -4189,13 +4189,16 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): cw_row = cw_index.row() self.shapes.clear(update=True) + for tooluid_key in self.cnc_tools: tooldia = float('%.4f' % float(self.cnc_tools[tooluid_key]['tooldia'])) gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed'] # tool_uid = int(self.ui.cnc_tools_table.item(cw_row, 3).text()) - if self.ui.cnc_tools_table.cellWidget((tooluid_key - 1), 6).isChecked(): - self.plot2(tooldia=tooldia, obj=self, visible=True, gcode_parsed=gcode_parsed) + for r in range(self.ui.cnc_tools_table.rowCount()): + if int(self.ui.cnc_tools_table.item(r, 5).text()) == int(tooluid_key): + if self.ui.cnc_tools_table.cellWidget(r, 6).isChecked(): + self.plot2(tooldia=tooldia, obj=self, visible=True, gcode_parsed=gcode_parsed) self.shapes.redraw() diff --git a/README.md b/README.md index 487303f1..97d31cdc 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ CAD program, and create G-Code for Isolation routing. - fixed Freeform Cutout gaps issue (it was double than the value set) - added protection so the Cutout (either Freeform or Rectangular) cannot be done on a multigeo Geometry - added 2Sided Tool default values in Edit -> Preferences -> Tools +- optimized the FlatCAMCNCJob.on_plot_cb_click_table() plot function and solved a bug regarding having tools numbers not in sync with the cnc tool table 28.01.2018