diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 7d79d81e..f1046a8e 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -124,6 +124,10 @@ class App(QtCore.QObject): # Manual URL manual_url = "http://flatcam.org/manual/index.html" video_url = "https://www.youtube.com/playlist?list=PLVvP2SYRpx-AQgNlfoxw93tXUXon7G94_" + gerber_spec_url ="https://www.ucamco.com/files/downloads/file/81/The_Gerber_File_Format_specification." \ + "pdf?7ac957791daba2cdf4c2c913f67a43da" + excellon_spec_url = "https://www.ucamco.com/files/downloads/file/305/the_xnc_file_format_specification.pdf" + bug_report_url = "https://bitbucket.org/jpcgt/flatcam/issues?status=new&status=open" # this variable will hold the project status # if True it will mean that the project was modified and not saved @@ -401,20 +405,15 @@ class App(QtCore.QObject): "global_portable": self.ui.general_defaults_form.general_app_group.portability_cb, "global_language": self.ui.general_defaults_form.general_app_group.language_cb, - "global_shell_at_startup": self.ui.general_defaults_form.general_app_group.shell_startup_cb, "global_version_check": self.ui.general_defaults_form.general_app_group.version_check_cb, "global_send_stats": self.ui.general_defaults_form.general_app_group.send_stats_cb, "global_pan_button": self.ui.general_defaults_form.general_app_group.pan_button_radio, "global_mselect_key": self.ui.general_defaults_form.general_app_group.mselect_radio, - "global_project_at_startup": self.ui.general_defaults_form.general_app_group.project_startup_cb, - "global_project_autohide": self.ui.general_defaults_form.general_app_group.project_autohide_cb, - "global_toggle_tooltips": self.ui.general_defaults_form.general_app_group.toggle_tooltips_cb, "global_worker_number": self.ui.general_defaults_form.general_app_group.worker_number_sb, "global_tolerance": self.ui.general_defaults_form.general_app_group.tol_entry, "global_open_style": self.ui.general_defaults_form.general_app_group.open_style_cb, - "global_delete_confirmation": self.ui.general_defaults_form.general_app_group.delete_conf_cb, "global_compression_level": self.ui.general_defaults_form.general_app_group.compress_combo, "global_save_compressed": self.ui.general_defaults_form.general_app_group.save_type_cb, @@ -442,15 +441,17 @@ class App(QtCore.QObject): "global_layout": self.ui.general_defaults_form.general_gui_set_group.layout_combo, "global_hover": self.ui.general_defaults_form.general_gui_set_group.hover_cb, "global_selection_shape": self.ui.general_defaults_form.general_gui_set_group.selection_cb, + "global_shell_at_startup": self.ui.general_defaults_form.general_gui_set_group.shell_startup_cb, + "global_project_at_startup": self.ui.general_defaults_form.general_gui_set_group.project_startup_cb, + "global_project_autohide": self.ui.general_defaults_form.general_gui_set_group.project_autohide_cb, + "global_toggle_tooltips": self.ui.general_defaults_form.general_gui_set_group.toggle_tooltips_cb, + "global_delete_confirmation": self.ui.general_defaults_form.general_gui_set_group.delete_conf_cb, # Gerber General "gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb, "gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb, "gerber_multicolored": self.ui.gerber_defaults_form.gerber_gen_group.multicolored_cb, "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry, - "gerber_buffering": self.ui.gerber_defaults_form.gerber_gen_group.buffering_radio, - "gerber_simplification": self.ui.gerber_defaults_form.gerber_gen_group.simplify_cb, - "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_gen_group.simplification_tol_spinner, # Gerber Options "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, @@ -468,6 +469,9 @@ class App(QtCore.QObject): # "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry, # "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry, "gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb, + "gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio, + "gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb, + "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner, # Gerber Export "gerber_exp_units": self.ui.gerber_defaults_form.gerber_exp_group.gerber_units_radio, @@ -513,9 +517,9 @@ class App(QtCore.QObject): # Excellon Options "excellon_drillz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry, "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry, + "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.eendz_entry, "excellon_feedrate": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_entry, "excellon_spindlespeed": self.ui.excellon_defaults_form.excellon_opt_group.spindlespeed_entry, - "excellon_spindledir": self.ui.excellon_defaults_form.excellon_opt_group.spindledir_radio, "excellon_dwell": self.ui.excellon_defaults_form.excellon_opt_group.dwell_cb, "excellon_dwelltime": self.ui.excellon_defaults_form.excellon_opt_group.dwelltime_entry, "excellon_toolchange": self.ui.excellon_defaults_form.excellon_opt_group.toolchange_cb, @@ -529,10 +533,10 @@ class App(QtCore.QObject): "excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry, "excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry, "excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry, - "excellon_endz": self.ui.excellon_defaults_form.excellon_adv_opt_group.eendz_entry, "excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry, "excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry, "excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry, + "excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio, "excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb, "excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb, @@ -585,23 +589,23 @@ class App(QtCore.QObject): "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry, "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.cncplunge_entry, "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry, - "geometry_spindledir": self.ui.geometry_defaults_form.geometry_opt_group.spindledir_radio, "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb, "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry, "geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb, "geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb, "geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry, + "geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.gendz_entry, "geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry, "geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb, # Geometry Advanced Options "geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry, "geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry, - "geometry_endz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gendz_entry, "geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.cncfeedrate_rapid_entry, "geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb, "geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry, "geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry, + "geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio, "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb, "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry, "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry, @@ -613,8 +617,6 @@ class App(QtCore.QObject): "cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb, "cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio, "cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb, - "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontsize_sp, - "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry, "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry, "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio, @@ -629,6 +631,8 @@ class App(QtCore.QObject): # CNC Job Advanced Options "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text, "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb, + "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp, + "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry, # NCC Tool "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, @@ -677,6 +681,7 @@ class App(QtCore.QObject): "tools_film_type": self.ui.tools_defaults_form.tools_film_group.film_type_radio, "tools_film_boundary": self.ui.tools_defaults_form.tools_film_group.film_boundary_entry, "tools_film_scale": self.ui.tools_defaults_form.tools_film_group.film_scale_entry, + "tools_film_color": self.ui.tools_defaults_form.tools_film_group.film_color_entry, # Panelize Tool "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns, @@ -846,6 +851,22 @@ class App(QtCore.QObject): "global_point_clipboard_format": "(%.4f, %.4f)", "global_zdownrate": None, + # autocomplete keywords + "global_autocomplete_keywords": + ['all', 'angle_x', 'angle_y', 'axis', 'axisoffset', 'box', 'center_x', 'center_y', + 'columns', 'combine', 'connect', 'contour', 'depthperpass', 'dia', 'diatol', 'dist', + 'drilled_dias', 'drillz', 'pp', + 'gridoffsety', 'gridx', 'gridy', 'has_offset', 'holes', 'margin', 'method', + 'milled_dias', + 'minoffset', 'multidepth', 'name', 'offset', 'opt_type', 'order', 'outname', + 'overlap', 'passes', 'postamble', 'ppname_e', 'ppname_g', 'preamble', 'radius', 'ref', + 'rest', 'rows', 'scale_factor', 'spacing_columns', 'spacing_rows', 'spindlespeed', + 'use_threads', 'value', 'x', 'x0', 'x1', 'y', 'y0', 'y1', 'z_cut', 'z_move', + 'default', 'feedrate_z', 'grbl_11', 'grbl_laser', 'hpgl', 'line_xyz', 'marlin', + 'Paste_1', 'Repetier', 'Toolchange_Custom', 'Roland_MDX_20', 'Toolchange_manual', + 'Toolchange_Probe_MACH3', 'dwell', 'dwelltime', 'toolchange_xy', 'iso_type', + 'Desktop', 'FlatPrj', 'FlatConfig', 'Users', 'Documents', 'My Documents', 'Marius' + ], # General GUI Settings "global_hover": False, "global_selection_shape": True, @@ -857,9 +878,6 @@ class App(QtCore.QObject): "gerber_multicolored": False, "gerber_circle_steps": 128, "gerber_use_buffer_for_union": True, - "gerber_buffering": "full", - "gerber_simplification": False, - "gerber_simp_tolerance": 0.0005, # Gerber Options "gerber_isotooldia": 0.00787402, @@ -877,6 +895,9 @@ class App(QtCore.QObject): "gerber_aperture_scale_factor": 1.0, "gerber_aperture_buffer_factor": 0.0, "gerber_follow": False, + "gerber_buffering": "full", + "gerber_simplification": False, + "gerber_simp_tolerance": 0.0005, # Gerber Export "gerber_exp_units": 'IN', @@ -917,9 +938,9 @@ class App(QtCore.QObject): # Excellon Options "excellon_drillz": -0.0590551, "excellon_travelz": 0.0787402, + "excellon_endz": 0.5, "excellon_feedrate": 3.14961, "excellon_spindlespeed": None, - "excellon_spindledir": 'CW', "excellon_dwell": False, "excellon_dwelltime": 1, "excellon_toolchange": False, @@ -933,10 +954,10 @@ class App(QtCore.QObject): "excellon_offset": 0.0, "excellon_toolchangexy": "0.0, 0.0", "excellon_startz": None, - "excellon_endz": 0.5, "excellon_feedrate_rapid": 31.4961, "excellon_z_pdepth": -0.02, "excellon_feedrate_probe": 3.14961, + "excellon_spindledir": 'CW', "excellon_f_plunge": False, "excellon_f_retract": False, @@ -983,10 +1004,10 @@ class App(QtCore.QObject): "geometry_travelz": 0.0787402, "geometry_toolchange": False, "geometry_toolchangez": 0.5, + "geometry_endz": 0.5, "geometry_feedrate": 3.14961, "geometry_feedrate_z": 3.14961, "geometry_spindlespeed": None, - "geometry_spindledir": 'CW', "geometry_dwell": False, "geometry_dwelltime": 1, "geometry_ppname_g": 'default', @@ -994,11 +1015,11 @@ class App(QtCore.QObject): # Geometry Advanced Options "geometry_toolchangexy": "0.0, 0.0", "geometry_startz": None, - "geometry_endz": 0.5, "geometry_feedrate_rapid": 3.14961, "geometry_extracut": False, "geometry_z_pdepth": -0.02, "geometry_f_plunge": False, + "geometry_spindledir": 'CW', "geometry_feedrate_probe": 3.14961, "geometry_segx": 0.0, "geometry_segy": 0.0, @@ -1010,8 +1031,6 @@ class App(QtCore.QObject): "cncjob_plot": True, "cncjob_plot_kind": 'all', "cncjob_annotation": True, - "cncjob_annotation_fontsize": 9, - "cncjob_annotation_fontcolor": '#990000', "cncjob_tooldia": 0.0393701, "cncjob_coords_type": "G90", "cncjob_coords_decimals": 4, @@ -1025,7 +1044,10 @@ class App(QtCore.QObject): # CNC Job Advanced Options "cncjob_toolchange_macro": "", "cncjob_toolchange_macro_enable": False, + "cncjob_annotation_fontsize": 9, + "cncjob_annotation_fontcolor": '#990000', + # NCC Tool "tools_ncctools": "0.0393701, 0.019685", "tools_nccorder": 'rev', "tools_nccoverlap": 0.015748, @@ -1044,6 +1066,7 @@ class App(QtCore.QObject): "tools_ncctipdia": 0.00393701, "tools_ncctipangle": 30, + # Cutout Tool "tools_cutouttooldia": 0.0944882, "tools_cutoutkind": "single", "tools_cutoutmargin": 0.00393701, @@ -1051,6 +1074,7 @@ class App(QtCore.QObject): "tools_gaps_ff": "4", "tools_cutout_convexshape": False, + # Paint Tool "tools_painttooldia": 0.023622, "tools_paintorder": 'rev', "tools_paintoverlap": 0.015748, @@ -1061,14 +1085,18 @@ class App(QtCore.QObject): "tools_paintcontour": True, "tools_paint_plotting": 'normal', + # 2-Sided Tool "tools_2sided_mirror_axis": "X", "tools_2sided_axis_loc": "point", "tools_2sided_drilldia": 0.0393701, + # Film Tool "tools_film_type": 'neg', "tools_film_boundary": 0.0393701, "tools_film_scale": 0, + "tools_film_color": '#000000', + # Panel Tool "tools_panelize_spacing_columns": 0, "tools_panelize_spacing_rows": 0, "tools_panelize_columns": 1, @@ -1078,6 +1106,7 @@ class App(QtCore.QObject): "tools_panelize_constrainy": 0.0, "tools_panelize_panel_type": 'gerber', + # Calculators Tool "tools_calc_vshape_tip_dia": 0.007874, "tools_calc_vshape_tip_angle": 30, "tools_calc_vshape_cut_z": 0.000787, @@ -1086,6 +1115,7 @@ class App(QtCore.QObject): "tools_calc_electro_cdensity": 13.0, "tools_calc_electro_growth": 10.0, + # Transform Tool "tools_transform_rotate": 90, "tools_transform_skew_x": 0.0, "tools_transform_skew_y": 0.0, @@ -1098,6 +1128,7 @@ class App(QtCore.QObject): "tools_transform_mirror_reference": False, "tools_transform_mirror_point": (0, 0), + # SolderPaste Tool "tools_solderpaste_tools": "0.0393701, 0.011811", "tools_solderpaste_new": 0.011811, "tools_solderpaste_z_start": 0.00019685039, @@ -1115,6 +1146,7 @@ class App(QtCore.QObject): "tools_solderpaste_dwellrev": 1, "tools_solderpaste_pp": 'Paste_1', + # Subtract Tool "tools_sub_close_paths": True, # file associations @@ -1204,9 +1236,9 @@ class App(QtCore.QObject): "excellon_drillz": self.ui.excellon_options_form.excellon_opt_group.cutz_entry, "excellon_travelz": self.ui.excellon_options_form.excellon_opt_group.travelz_entry, + "excellon_endz": self.ui.excellon_options_form.excellon_opt_group.eendz_entry, "excellon_feedrate": self.ui.excellon_options_form.excellon_opt_group.feedrate_entry, "excellon_spindlespeed": self.ui.excellon_options_form.excellon_opt_group.spindlespeed_entry, - "excellon_spindledir": self.ui.excellon_options_form.excellon_opt_group.spindledir_radio, "excellon_dwell": self.ui.excellon_options_form.excellon_opt_group.dwell_cb, "excellon_dwelltime": self.ui.excellon_options_form.excellon_opt_group.dwelltime_entry, "excellon_toolchange": self.ui.excellon_options_form.excellon_opt_group.toolchange_cb, @@ -1218,7 +1250,7 @@ class App(QtCore.QObject): "excellon_toolchangexy": self.ui.excellon_options_form.excellon_adv_opt_group.toolchangexy_entry, "excellon_f_plunge": self.ui.excellon_options_form.excellon_adv_opt_group.fplunge_cb, "excellon_startz": self.ui.excellon_options_form.excellon_adv_opt_group.estartz_entry, - "excellon_endz": self.ui.excellon_options_form.excellon_adv_opt_group.eendz_entry, + "excellon_spindledir": self.ui.excellon_options_form.excellon_adv_opt_group.spindledir_radio, "geometry_plot": self.ui.geometry_options_form.geometry_gen_group.plot_cb, "geometry_cnctooldia": self.ui.geometry_options_form.geometry_gen_group.cnctooldia_entry, @@ -1228,12 +1260,12 @@ class App(QtCore.QObject): "geometry_feedrate": self.ui.geometry_options_form.geometry_opt_group.cncfeedrate_entry, "geometry_feedrate_z": self.ui.geometry_options_form.geometry_opt_group.cncplunge_entry, "geometry_spindlespeed": self.ui.geometry_options_form.geometry_opt_group.cncspindlespeed_entry, - "geometry_spindledir": self.ui.geometry_options_form.geometry_opt_group.spindledir_radio, "geometry_dwell": self.ui.geometry_options_form.geometry_opt_group.dwell_cb, "geometry_dwelltime": self.ui.geometry_options_form.geometry_opt_group.dwelltime_entry, "geometry_ppname_g": self.ui.geometry_options_form.geometry_opt_group.pp_geometry_name_cb, "geometry_toolchange": self.ui.geometry_options_form.geometry_opt_group.toolchange_cb, "geometry_toolchangez": self.ui.geometry_options_form.geometry_opt_group.toolchangez_entry, + "geometry_endz": self.ui.geometry_options_form.geometry_opt_group.gendz_entry, "geometry_depthperpass": self.ui.geometry_options_form.geometry_opt_group.depthperpass_entry, "geometry_multidepth": self.ui.geometry_options_form.geometry_opt_group.multidepth_cb, @@ -1241,9 +1273,9 @@ class App(QtCore.QObject): "geometry_segy": self.ui.geometry_options_form.geometry_adv_opt_group.segy_entry, "geometry_feedrate_rapid": self.ui.geometry_options_form.geometry_adv_opt_group.cncfeedrate_rapid_entry, "geometry_f_plunge": self.ui.geometry_options_form.geometry_adv_opt_group.fplunge_cb, + "geometry_spindledir": self.ui.geometry_options_form.geometry_adv_opt_group.spindledir_radio, "geometry_toolchangexy": self.ui.geometry_options_form.geometry_adv_opt_group.toolchangexy_entry, "geometry_startz": self.ui.geometry_options_form.geometry_adv_opt_group.gstartz_entry, - "geometry_endz": self.ui.geometry_options_form.geometry_adv_opt_group.gendz_entry, "geometry_extracut": self.ui.geometry_options_form.geometry_adv_opt_group.extracut_cb, "cncjob_plot": self.ui.cncjob_options_form.cncjob_gen_group.plot_cb, @@ -1487,10 +1519,17 @@ class App(QtCore.QObject): "background-color:%s" % str(self.defaults['global_proj_item_dis_color'])[:7]) # Init the Annotation CNC Job color - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry.set_value( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value( self.defaults['cncjob_annotation_fontcolor']) - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_button.setStyleSheet( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet( "background-color:%s" % str(self.defaults['cncjob_annotation_fontcolor'])[:7]) + + # Init the Tool Film color + self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value( + self.defaults['tools_film_color']) + self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_film_color'])[:7]) + # ### End of Data #### # ############################################### @@ -1671,6 +1710,9 @@ class App(QtCore.QObject): self.ui.menuhelp_about.triggered.connect(self.on_about) self.ui.menuhelp_home.triggered.connect(lambda: webbrowser.open(self.app_url)) self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.manual_url)) + self.ui.menuhelp_report_bug.triggered.connect(lambda: webbrowser.open(self.bug_report_url)) + self.ui.menuhelp_exc_spec.triggered.connect(lambda: webbrowser.open(self.excellon_spec_url)) + self.ui.menuhelp_gerber_spec.triggered.connect(lambda: webbrowser.open(self.gerber_spec_url)) self.ui.menuhelp_videohelp.triggered.connect(lambda: webbrowser.open(self.video_url)) self.ui.menuhelp_shortcut_list.triggered.connect(self.on_shortcut_list) @@ -1809,11 +1851,17 @@ class App(QtCore.QObject): # ########## CNC Job related signals ############# self.ui.cncjob_defaults_form.cncjob_adv_opt_group.tc_variable_combo.currentIndexChanged[str].connect( self.on_cnc_custom_parameters) - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry.editingFinished.connect( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.editingFinished.connect( self.on_annotation_fontcolor_entry) - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_button.clicked.connect( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.clicked.connect( self.on_annotation_fontcolor_button) + # ########## Tools related signals ############# + self.ui.tools_defaults_form.tools_film_group.film_color_entry.editingFinished.connect( + self.on_film_color_entry) + self.ui.tools_defaults_form.tools_film_group.film_color_button.clicked.connect( + self.on_film_color_button) + # ########## Modify G-CODE Plot Area TAB ########### self.ui.code_editor.textChanged.connect(self.handleTextChanged) self.ui.buttonOpen.clicked.connect(self.handleOpen) @@ -1830,7 +1878,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.ui.general_defaults_form.general_app_group.shell_startup_cb.clicked.connect(self.on_toggle_shell) + self.ui.general_defaults_form.general_gui_set_group.shell_startup_cb.clicked.connect(self.on_toggle_shell) # Make sure that when the Excellon loading parameters are changed, the change is reflected in the # Export Excellon parameters. @@ -1900,27 +1948,13 @@ class App(QtCore.QObject): 'mirror', 'ncc', 'ncc_clear', 'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset', 'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'options', 'paint', - 'pan', 'panel', 'panelize', 'plot', 'save', 'save_project', 'save_sys', 'scale', + 'pan', 'panel', 'panelize', 'plot_all', 'plot_objects', 'save', 'save_project', + 'save_sys', 'scale', 'set_active', 'set_sys', 'setsys', 'skew', 'subtract_poly', 'subtract_rectangle', 'version', 'write_gcode' ] - self.ordinary_keywords = ['all', 'angle_x', 'angle_y', 'axis', 'axisoffset', 'box', 'center_x', 'center_y', - 'columns', 'combine', 'connect', 'contour', 'depthperpass', 'dia', 'diatol', 'dist', - 'drilled_dias', 'drillz', 'pp', - 'endz', 'extracut', 'factor', 'False', 'false', 'feedrate', 'feedrate_rapid', - 'filename', 'follow', 'gaps', 'gapsize', 'grid', 'gridoffset', 'gridoffsetx', - 'gridoffsety', 'gridx', 'gridy', 'has_offset', 'holes', 'margin', 'method', - 'milled_dias', - 'minoffset', 'multidepth', 'name', 'offset', 'opt_type', 'order', 'outname', - 'overlap', 'passes', 'postamble', 'ppname_e', 'ppname_g', 'preamble', 'radius', 'ref', - 'rest', 'rows', 'scale_factor', 'spacing_columns', 'spacing_rows', 'spindlespeed', - 'toolchange', 'toolchangez', 'tooldia', 'tools', 'travelz', 'True', 'true', 'type', - 'use_threads', 'value', 'x', 'x0', 'x1', 'y', 'y0', 'y1', 'z_cut', 'z_move', - 'default', 'feedrate_z', 'grbl_11', 'grbl_laser', 'hpgl', 'line_xyz', 'marlin', - 'Paste_1', 'Repetier', 'Toolchange_Custom', 'Roland_MDX_20', 'Toolchange_manual', - 'Toolchange_Probe_MACH3', 'dwell', 'dwelltime', 'toolchange_xy' - ] + self.ordinary_keywords = self.defaults["global_autocomplete_keywords"] self.tcl_keywords = [ 'after', 'append', 'apply', 'argc', 'argv', 'argv0', 'array', 'attemptckalloc', 'attemptckrealloc', @@ -2255,6 +2289,10 @@ class App(QtCore.QObject): # Variable to store the GCODE that was edited self.gcode_edited = "" + # reference for the self.ui.code_editor + self.reference_code_editor = None + self.script_code = '' + # if Preferences are changed in the Edit -> Preferences tab the value will be set to True self.preferences_changed_flag = False @@ -3064,8 +3102,8 @@ class App(QtCore.QObject): result = self.exec_command_test(text, False) # MS: added this method call so the geometry is updated once the TCL command is executed - if no_plot is None: - self.plot_all() + # if no_plot is None: + # self.plot_all() return result @@ -5554,8 +5592,8 @@ class App(QtCore.QObject): def on_annotation_fontcolor_entry(self): self.defaults['cncjob_annotation_fontcolor'] = \ - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry.get_value() - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_button.setStyleSheet( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.get_value() + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet( "background-color:%s" % str(self.defaults['cncjob_annotation_fontcolor'])) def on_annotation_fontcolor_button(self): @@ -5567,12 +5605,38 @@ class App(QtCore.QObject): if annotation_color.isValid() is False: return - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_button.setStyleSheet( + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet( "background-color:%s" % str(annotation_color.name())) new_val_sel = str(annotation_color.name()) - self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_fontcolor_entry.set_value(new_val_sel) - self.defaults['global_proj_item_dis_color'] = new_val_sel + self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(new_val_sel) + self.defaults['cncjob_annotation_fontcolor'] = new_val_sel + + def on_film_color_entry(self): + self.defaults['tools_film_color'] = \ + self.ui.tools_defaults_form.tools_film_group.film_color_entry.get_value() + self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( + "background-color:%s" % str(self.defaults['tools_film_color'])) + + def on_film_color_button(self): + current_color = QtGui.QColor(self.defaults['tools_film_color']) + + c_dialog = QtWidgets.QColorDialog() + film_color = c_dialog.getColor(initial=current_color) + + if film_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if film_color != current_color: + self.on_preferences_edited() + + self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( + "background-color:%s" % str(film_color.name())) + + new_val_sel = str(film_color.name()) + self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(new_val_sel) + self.defaults['tools_film_color'] = new_val_sel def on_splash_changed(self, state): settings = QSettings("Open Source", "FlatCAM") @@ -5915,11 +5979,11 @@ class App(QtCore.QObject): if self.ui.shell_dock.isHidden(): self.ui.shell_dock.show() - script_code = self.ui.code_editor.toPlainText() + self.script_code = deepcopy(self.ui.code_editor.toPlainText()) # self.shell._sysShell.exec_command(script_code) old_line = '' - for tcl_command_line in script_code.splitlines(): + for tcl_command_line in self.script_code.splitlines(): # do not process lines starting with '#' = comment and empty lines if not tcl_command_line.startswith('#') and tcl_command_line != '': # id FlatCAM is run in Windows then replace all the slashes with @@ -5942,17 +6006,16 @@ class App(QtCore.QObject): self.shell.append_output(result + '\n') old_line = '' - except tk.TclError as e: + except tk.TclError: old_line = old_line + tcl_command_line + '\n' + except Exception as e: + log.debug("App.handleRunCode() --> %s" % str(e)) if old_line != '': # it means that the script finished with an error result = self.tcl.eval("set errorInfo") self.log.error("Exec command Exception: %s" % (result + '\n')) self.shell.append_error('ERROR: ' + result + '\n') - else: - # success! plot all objects - self.plot_all() self.shell.close_proccessing() @@ -7522,7 +7585,7 @@ class App(QtCore.QObject): self.inform.emit('[success] %s...' % _("New Project created")) - def on_file_new(self): + def on_file_new(self, cli=None): """ Callback for menu item File->New. Returns the application to its startup state. This method is thread-safe. @@ -7582,15 +7645,16 @@ class App(QtCore.QObject): # Init Tools self.init_tools() - # Close any Tabs opened in the Plot Tab Area section - for index in range(self.ui.plot_tab_area.count()): - self.ui.plot_tab_area.closeTab(index) - # for whatever reason previous command does not close the last tab so I do it manually - self.ui.plot_tab_area.closeTab(0) + if cli is None: + # Close any Tabs opened in the Plot Tab Area section + for index in range(self.ui.plot_tab_area.count()): + self.ui.plot_tab_area.closeTab(index) + # for whatever reason previous command does not close the last tab so I do it manually + self.ui.plot_tab_area.closeTab(0) - # # And then add again the Plot Area - self.ui.plot_tab_area.addTab(self.ui.plot_tab, "Plot Area") - self.ui.plot_tab_area.protectTab(0) + # # And then add again the Plot Area + self.ui.plot_tab_area.addTab(self.ui.plot_tab, "Plot Area") + self.ui.plot_tab_area.protectTab(0) # take the focus of the Notebook on Project Tab. self.ui.notebook.setCurrentWidget(self.ui.project_tab) @@ -8201,6 +8265,9 @@ class App(QtCore.QObject): self.ui.code_editor.completer_enable = False self.ui.buttonRun.hide() + # make sure to keep a reference to the code editor + self.reference_code_editor = self.ui.code_editor + # Switch plot_area to CNCJob tab self.ui.plot_tab_area.setCurrentWidget(self.ui.cncjob_tab) @@ -8219,20 +8286,37 @@ class App(QtCore.QObject): if obj.kind == 'gerber': flt = "Gerber Files (*.GBR);;All Files (*.*)" - else: + elif obj.kind == 'excellon': flt = "Excellon Files (*.DRL);;All Files (*.*)" + elif obj.kind == 'cncjob': + "GCode Files (*.NC);;All Files (*.*)" + else: + flt = "All Files (*.*)" self.init_code_editor(name=_("Source Editor")) self.ui.buttonOpen.clicked.connect(lambda: self.handleOpen(filt=flt)) self.ui.buttonSave.clicked.connect(lambda: self.handleSaveGCode(filt=flt)) # then append the text from GCode to the text editor - try: - file = StringIO(obj.source_file) - except AttributeError: - self.inform.emit('[WARNING_NOTCL] %s' % - _("There is no selected object for which to see it's source file code.")) - return 'fail' + if obj.kind == 'cncjob': + try: + file = obj.export_gcode( + preamble=self.defaults["cncjob_prepend"], + postamble=self.defaults["cncjob_append"], + to_file=True) + if file == 'fail': + return 'fail' + except AttributeError: + self.inform.emit('[WARNING_NOTCL] %s' % + _("There is no selected object for which to see it's source file code.")) + return 'fail' + else: + try: + file = StringIO(obj.source_file) + except AttributeError: + self.inform.emit('[WARNING_NOTCL] %s' % + _("There is no selected object for which to see it's source file code.")) + return 'fail' self.ui.cncjob_frame.hide() try: @@ -8289,6 +8373,10 @@ class App(QtCore.QObject): self.ui.buttonOpen.clicked.connect(lambda: self.handleOpen(filt=flt)) self.ui.buttonSave.clicked.connect(lambda: self.handleSaveGCode(filt=flt)) self.ui.buttonRun.show() + try: + self.ui.buttonRun.clicked.disconnect(self.handleRunCode) + except TypeError: + pass self.ui.buttonRun.clicked.connect(self.handleRunCode) self.handleTextChanged() @@ -8556,8 +8644,6 @@ class App(QtCore.QObject): def make_negative_film(): exported_svg = obj.export_svg(scale_factor=scale_factor) - self.progress.emit(40) - # Determine bounding area for svg export bounds = box.bounds() size = box.size() @@ -8583,8 +8669,6 @@ class App(QtCore.QObject): svg_header += '' svg_footer = ' ' - self.progress.emit(60) - # Change the attributes of the exported SVG # We don't need stroke-width - wrong, we do when we have lines with certain width # We set opacity to maximum @@ -8613,7 +8697,6 @@ class App(QtCore.QObject): exported_svg = ET.tostring(root) svg_elem = svg_header + str(exported_svg) + svg_footer - self.progress.emit(80) # Parse the xml through a xml parser just to add line feeds # and to make it look more pretty for the output @@ -8627,7 +8710,6 @@ class App(QtCore.QObject): "Most likely another app is holding the file open and not accessible.")) return 'fail' - self.progress.emit(100) if self.defaults["global_open_style"] is False: self.file_opened.emit("SVG", filename) self.file_saved.emit("SVG", filename) @@ -8649,7 +8731,7 @@ class App(QtCore.QObject): else: make_negative_film() - def export_svg_black(self, obj_name, box_name, filename, scale_factor=0.00, use_thread=True): + def export_svg_positive(self, obj_name, box_name, filename, scale_factor=0.00, use_thread=True): """ Exports a Geometry Object to an SVG file in positive black. @@ -8660,7 +8742,7 @@ class App(QtCore.QObject): :param use_thread: if to be run in a separate thread; boolean :return: """ - self.report_usage("export_svg_black()") + self.report_usage("export_svg_positive()") if filename is None: filename = self.defaults["global_last_save_folder"] @@ -8684,7 +8766,7 @@ class App(QtCore.QObject): (_("No object Box. Using instead"), obj)) box = obj - def make_black_film(): + def make_positive_film(): exported_svg = obj.export_svg(scale_factor=scale_factor) self.progress.emit(40) @@ -8695,9 +8777,9 @@ class App(QtCore.QObject): # We set the colour to WHITE root = ET.fromstring(exported_svg) for child in root: - child.set('fill', '#000000') + child.set('fill', str(self.defaults['tools_film_color'])) child.set('opacity', '1.0') - child.set('stroke', '#000000') + child.set('stroke', str(self.defaults['tools_film_color'])) exported_svg = ET.tostring(root) @@ -8765,7 +8847,7 @@ class App(QtCore.QObject): def job_thread_film(app_obj): try: - make_black_film() + make_positive_film() except Exception as e: proc.done() return @@ -8773,7 +8855,7 @@ class App(QtCore.QObject): self.worker_task.emit({'fcn': job_thread_film, 'params': [self]}) else: - make_black_film() + make_positive_film() def save_source_file(self, obj_name, filename, use_thread=True): """ @@ -9375,7 +9457,7 @@ class App(QtCore.QObject): self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) - def open_excellon(self, filename, outname=None): + def open_excellon(self, filename, outname=None, plot=True): """ Opens an Excellon file, parses it and creates a new object for it in the program. Thread-safe. @@ -9429,7 +9511,7 @@ class App(QtCore.QObject): # Object name name = outname or filename.split('/')[-1].split('\\')[-1] - ret_val = self.new_object("excellon", name, obj_init, autoselected=False) + ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': self.inform.emit('[ERROR_NOTCL] %s' % _('Open Excellon file failed. Probable not an Excellon file.')) @@ -9442,7 +9524,7 @@ class App(QtCore.QObject): self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) - def open_gcode(self, filename, outname=None): + def open_gcode(self, filename, outname=None, plot=True): """ Opens a G-gcode file, parses it and creates a new object for it in the program. Thread-safe. @@ -9494,7 +9576,7 @@ class App(QtCore.QObject): name = outname or filename.split('/')[-1].split('\\')[-1] # New object creation and file processing - ret = self.new_object("cncjob", name, obj_init, autoselected=False) + ret = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) if ret == 'fail': self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to create CNCJob Object. Probable not a GCode file.\n " @@ -9542,7 +9624,7 @@ class App(QtCore.QObject): (_("Failed to open config file"), filename)) return - def open_project(self, filename, run_from_arg=None): + def open_project(self, filename, run_from_arg=None, plot=True, cli=None): """ Loads a project from the specified file. @@ -9551,16 +9633,21 @@ class App(QtCore.QObject): 3) Calls on_file_new() 4) Updates options 5) Calls new_object() with the object's from_dict() as init method. - 6) Calls plot_all() + 6) Calls plot_all() if plot=True :param filename: Name of the file from which to load. :type filename: str :param run_from_arg: True if run for arguments + :param plot: If True plot all objects in the project + :param cli: run from command line :return: None """ App.log.debug("Opening project: " + filename) - self.set_ui_title(name=_("Loading Project ... Please Wait ...")) + # for some reason, setting ui_title does not work when this method is called from Tcl Shell + # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled) + if cli is None: + self.set_ui_title(name=_("Loading Project ... Please Wait ...")) # Open and parse an uncompressed Project file try: @@ -9583,7 +9670,6 @@ class App(QtCore.QObject): with lzma.open(filename) as f: file_content = f.read().decode('utf-8') d = json.loads(file_content, object_hook=dict2obj) - except Exception as e: App.log.error("Failed to open project file: %s with error: %s" % (filename, str(e))) self.inform.emit('[ERROR_NOTCL] %s: %s' % @@ -9594,13 +9680,19 @@ class App(QtCore.QObject): # # NOT THREAD SAFE # ## if run_from_arg is True: pass + elif cli is True: + self.delete_selection_shape() else: self.on_file_new() # Project options self.options.update(d['options']) self.project_filename = filename - self.set_screen_units(self.options["units"]) + + # for some reason, setting ui_title does not work when this method is called from Tcl Shell + # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled) + if cli is None: + self.set_screen_units(self.options["units"]) # Re create objects App.log.debug(" **************** Started PROEJCT loading... **************** ") @@ -9608,24 +9700,31 @@ class App(QtCore.QObject): for obj in d['objs']: def obj_init(obj_inst, app_inst): obj_inst.from_dict(obj) + App.log.debug("Recreating from opened project an %s object: %s" % (obj['kind'].capitalize(), obj['options']['name'])) - self.set_ui_title(name="{} {}: {}".format(_("Loading Project ... restoring"), - obj['kind'].upper(), - obj['options']['name'] - ) - ) + # for some reason, setting ui_title does not work when this method is called from Tcl Shell + # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled) + if cli is None: + self.set_ui_title(name="{} {}: {}".format(_("Loading Project ... restoring"), + obj['kind'].upper(), + obj['options']['name'] + ) + ) - self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=True) + self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=plot) - # self.plot_all() self.inform.emit('[success] %s: %s' % (_("Project loaded from"), filename)) self.should_we_save = False self.file_opened.emit("project", filename) - self.set_ui_title(name=self.project_filename) + + # for some reason, setting ui_title does not work when this method is called from Tcl Shell + # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled) + if cli is None: + self.set_ui_title(name=self.project_filename) App.log.debug(" **************** Finished PROJECT loading... **************** ") diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 8c9844fc..b6754848 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -600,7 +600,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText(_( - 'Basic' + '%s' % _('Basic') )) self.ui.apertures_table_label.hide() self.ui.aperture_table_visibility_cb.hide() @@ -613,7 +613,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui.except_cb.hide() else: self.ui.level.setText(_( - 'Advanced' + '%s' % _('Advanced') )) if self.app.defaults["gerber_buffering"] == 'no': @@ -923,8 +923,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): except Exception as e: return "Operation failed: %s" % str(e) - def isolate(self, iso_type=None, dia=None, passes=None, overlap=None, - outname=None, combine=None, milling_type=None, follow=None): + def isolate(self, iso_type=None, dia=None, passes=None, overlap=None, outname=None, combine=None, + milling_type=None, follow=None, plot=True): """ Creates an isolation routing geometry object in the project. @@ -947,13 +947,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber): combine = bool(combine) if milling_type is None: milling_type = self.options["milling_type"] + if iso_type is None: self.iso_type = 2 else: self.iso_type = iso_type - base_name = self.options["name"] + "_iso" - base_name = outname or base_name + base_name = self.options["name"] def generate_envelope(offset, invert, envelope_iso_type=2, follow=None, passes=0): # isolation_geometry produces an envelope that is going on the left of the geometry @@ -1061,12 +1061,15 @@ class FlatCAMGerber(FlatCAMObj, Gerber): return new_geometry if combine: - if self.iso_type == 0: - iso_name = self.options["name"] + "_ext_iso" - elif self.iso_type == 1: - iso_name = self.options["name"] + "_int_iso" + if outname is None: + if self.iso_type == 0: + iso_name = base_name + "_ext_iso" + elif self.iso_type == 1: + iso_name = base_name + "_int_iso" + else: + iso_name = base_name + "_iso" else: - iso_name = base_name + iso_name = outname # TODO: This is ugly. Create way to pass data into init function. def iso_init(geo_obj, app_obj): @@ -1161,25 +1164,31 @@ class FlatCAMGerber(FlatCAMObj, Gerber): geo_obj.solid_geometry = area_subtraction(geo_obj.solid_geometry) # TODO: Do something if this is None. Offer changing name? - self.app.new_object("geometry", iso_name, iso_init) + self.app.new_object("geometry", iso_name, iso_init, plot=plot) else: for i in range(passes): offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia) if passes > 1: - if self.iso_type == 0: - iso_name = self.options["name"] + "_ext_iso" + str(i + 1) - elif self.iso_type == 1: - iso_name = self.options["name"] + "_int_iso" + str(i + 1) + if outname is None: + if self.iso_type == 0: + iso_name = base_name + "_ext_iso" + str(i + 1) + elif self.iso_type == 1: + iso_name = base_name + "_int_iso" + str(i + 1) + else: + iso_name = base_name + "_iso" + str(i + 1) else: - iso_name = base_name + str(i + 1) + iso_name = outname else: - if self.iso_type == 0: - iso_name = self.options["name"] + "_ext_iso" - elif self.iso_type == 1: - iso_name = self.options["name"] + "_int_iso" + if outname is None: + if self.iso_type == 0: + iso_name = base_name + "_ext_iso" + elif self.iso_type == 1: + iso_name = base_name + "_int_iso" + else: + iso_name = base_name + "_iso" else: - iso_name = base_name + iso_name = outname # TODO: This is ugly. Create way to pass data into init function. def iso_init(geo_obj, app_obj): @@ -1230,7 +1239,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber): geo_obj.solid_geometry = area_subtraction(geo_obj.solid_geometry) # TODO: Do something if this is None. Offer changing name? - self.app.new_object("geometry", iso_name, iso_init) + self.app.new_object("geometry", iso_name, iso_init, plot=plot) def on_plot_cb_click(self, *args): if self.muted_ui: @@ -2316,14 +2325,12 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText(_( - 'Basic' + '%s' % _('Basic') )) self.ui.tools_table.setColumnHidden(4, True) self.ui.estartz_label.hide() self.ui.estartz_entry.hide() - self.ui.eendz_label.hide() - self.ui.eendz_entry.hide() self.ui.feedrate_rapid_label.hide() self.ui.feedrate_rapid_entry.hide() self.ui.pdepth_label.hide() @@ -2332,7 +2339,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.ui.feedrate_probe_entry.hide() else: self.ui.level.setText(_( - 'Advanced' + '%s' % _('Advanced') )) assert isinstance(self.ui, ExcellonObjectUI), \ @@ -2595,7 +2602,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): return has_slots, excellon_code - def generate_milling_drills(self, tools=None, outname=None, tooldia=None, use_thread=False): + def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): """ Note: This method is a good template for generic operations as it takes it's options from parameters or otherwise from the @@ -2674,7 +2681,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): Point(hole['point']).buffer(buffer_value).exterior) if use_thread: def geo_thread(app_obj): - app_obj.new_object("geometry", outname, geo_init) + app_obj.new_object("geometry", outname, geo_init, plot=plot) app_obj.progress.emit(100) # Create a promise with the new name @@ -2683,11 +2690,11 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # Send to worker self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]}) else: - self.app.new_object("geometry", outname, geo_init) + self.app.new_object("geometry", outname, geo_init, plot=plot) return True, "" - def generate_milling_slots(self, tools=None, outname=None, tooldia=None, use_thread=False): + def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=True, use_thread=False): """ Note: This method is a good template for generic operations as it takes it's options from parameters or otherwise from the @@ -2781,7 +2788,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): if use_thread: def geo_thread(app_obj): - app_obj.new_object("geometry", outname + '_slot', geo_init) + app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot) app_obj.progress.emit(100) # Create a promise with the new name @@ -2790,7 +2797,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # Send to worker self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]}) else: - self.app.new_object("geometry", outname + '_slot', geo_init) + self.app.new_object("geometry", outname + '_slot', geo_init, plot=plot) return True, "" @@ -3596,7 +3603,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText(_( - 'Basic' + '%s' % _('Basic') )) self.ui.geo_tools_table.setColumnHidden(2, True) @@ -3607,8 +3614,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.ui.addtool_btn.hide() self.ui.copytool_btn.hide() self.ui.deltool_btn.hide() - self.ui.endzlabel.hide() - self.ui.gendz_entry.hide() + # self.ui.endzlabel.hide() + # self.ui.gendz_entry.hide() self.ui.fr_rapidlabel.hide() self.ui.cncfeedrate_rapid_entry.hide() self.ui.extracut_cb.hide() @@ -3618,7 +3625,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.ui.feedrate_probe_entry.hide() else: self.ui.level.setText(_( - 'Advanced' + '%s' % _('Advanced') )) self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click) @@ -4518,7 +4525,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No tool selected in the tool table ...")) - def mtool_gen_cncjob(self, outname=None, tools_dict=None, tools_in_use=None, segx=None, segy=None, use_thread=True): + def mtool_gen_cncjob(self, outname=None, tools_dict=None, tools_in_use=None, segx=None, segy=None, + plot=True, use_thread=True): """ Creates a multi-tool CNCJob out of this Geometry object. The actual work is done by the target FlatCAMCNCjob object's @@ -4875,7 +4883,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): def job_thread(app_obj): if self.solid_geometry: with self.app.proc_container.new(_("Generating CNC Code")): - if app_obj.new_object("cncjob", outname, job_init_single_geometry) != 'fail': + if app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail': app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname) app_obj.progress.emit(100) @@ -4892,22 +4900,23 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: if self.solid_geometry: - self.app.new_object("cncjob", outname, job_init_single_geometry) + self.app.new_object("cncjob", outname, job_init_single_geometry, plot=plot) else: - self.app.new_object("cncjob", outname, job_init_multi_geometry) + self.app.new_object("cncjob", outname, job_init_multi_geometry, plot=plot) def generatecncjob( self, outname=None, - tooldia=None, offset=None, + dia=None, offset=None, z_cut=None, z_move=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, spindlespeed=None, dwell=None, dwelltime=None, multidepth=None, depthperpass=None, toolchange=None, toolchangez=None, toolchangexy=None, extracut=None, startz=None, endz=None, - ppname_g=None, + pp=None, segx=None, segy=None, - use_thread=True): + use_thread=True, + plot=True): """ Only used for TCL Command. Creates a CNCJob out of this Geometry object. The actual @@ -4919,14 +4928,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): :param feedrate: Feed rate while cutting on X - Y plane :param feedrate_z: Feed rate while cutting on Z plane :param feedrate_rapid: Feed rate while moving with rapids - :param tooldia: Tool diameter + :param dia: Tool diameter :param outname: Name of the new object :param spindlespeed: Spindle speed (RPM) - :param ppname_g Name of the postprocessor + :param pp Name of the postprocessor :return: None """ - tooldia = tooldia if tooldia else float(self.options["cnctooldia"]) + tooldia = dia if dia else float(self.options["cnctooldia"]) outname = outname if outname is not None else self.options["name"] z_cut = z_cut if z_cut is not None else float(self.options["cutz"]) @@ -4957,7 +4966,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): dwell = dwell if dwell else self.options["dwell"] dwelltime = dwelltime if dwelltime else float(self.options["dwelltime"]) - ppname_g = ppname_g if ppname_g else self.options["ppname_g"] + ppname_g = pp if pp else self.options["ppname_g"] # Object initialization function for app.new_object() # RUNNING ON SEPARATE THREAD! @@ -5034,7 +5043,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # To be run in separate thread def job_thread(app_obj): with self.app.proc_container.new(_("Generating CNC Code")): - app_obj.new_object("cncjob", outname, job_init) + app_obj.new_object("cncjob", outname, job_init, plot=plot) app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname) app_obj.progress.emit(100) @@ -5044,7 +5053,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # Send to worker self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - self.app.new_object("cncjob", outname, job_init) + self.app.new_object("cncjob", outname, job_init, plot=plot) # def on_plot_cb_click(self, *args): # TODO: args not needed # if self.muted_ui: @@ -5833,11 +5842,12 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): preamble = str(self.ui.prepend_text.get_value()) postamble = str(self.ui.append_text.get_value()) - gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True) - if gc == 'fail': + + gco = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True) + if gco == 'fail': return else: - self.app.gcode_edited = gc + self.app.gcode_edited = gco self.app.init_code_editor(name=_("Code Editor")) self.app.ui.buttonOpen.clicked.connect(self.app.handleOpen) diff --git a/ObjectCollection.py b/ObjectCollection.py index 9fe589e1..f85e9dea 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -320,7 +320,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): self.app.ui.menuprojectgeneratecnc.setVisible(False) if type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMGerber: self.app.ui.menuprojectedit.setVisible(False) - if type(obj) != FlatCAMGerber and type(obj) != FlatCAMExcellon: + if type(obj) != FlatCAMGerber and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMCNCjob: self.app.ui.menuprojectviewsource.setVisible(False) else: self.app.ui.menuprojectgeneratecnc.setVisible(False) diff --git a/README.md b/README.md index 0f851261..c67d8997 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,18 @@ CAD program, and create G-Code for Isolation routing. ================================================= +16.09.2019 + +- modified the TclCommand New so it will no longer close all tabs when called (it closed the Code Editor tab which may have been holding the code that run) +- fixed the App.on_view_source() method for CNCJob objects: the Gcode will now contain the Prepend and Append code from the Edit -> Preferences -> CNCJob -> CNCJob Options +- added a new parameter named 'muted' for the TclCommands: cncjob, drillcncjob and write_gcode. Setting it as -muted 1 will disable the error reporting in TCL Shell +- some GUI optimizations +- more GUI optimizations related to being part of the Advanced category or not +- added possibility to change the positive SVG exported file color in Tool Film +- fixed some issues recently introduced in the TclCommands CNCJob, DrillCNCJob and write_gcode; changed some parameters names +- fixed issue in the Laser postprocessor where the laser was turned on as soon as the GCode started creating an unwanted cut up until the job start +- added new links in Menu -> Help (Excellon, Gerber specifications and a Report Bug) + 15.09.2019 - refactored FlatCAMGeometry.mtool_gen_cncjob() method @@ -19,6 +31,18 @@ CAD program, and create G-Code for Isolation routing. - finished updating the TclCommand cncjob to work for multi-geo Geometry objects with the parameters from the args - fixed the TclCommand cncjob to use the -outname parameter - added some more keywords in the data_model for auto-completer +- fixed isolate TclCommand to use correctly the -outname parameter +- added possibility to see the GCode when right clicking on the Project tab on a CNCJob object and then clicking View Source +- added a new TclCommand named PlotObjects which will plot a list of FlatCAM objects +- made that after opening an object in FlatCAM it is not automatically plotted. If the user wants to plot it can use the TclCommands PlotAll or PlotObjects +- modified the TclCommands so that open files do not plot the opened files automatically +- made all TclCommands not to be plotted automatically +- made sure that all TclCommands are not threaded +- added new TclCommands: NewExcellon, NewGerber +- fixed the TclCommand open_project +- added the outname parameter (and established an default name when outname not used) for the AlignDrillGrid and AlignDrill TclCommands +- fixed Scripts repeating multiple time when the Code Editor is used. This repetition was correlated with multiple openings of the Code Editor window (especially after an error) +- added the autocomplete keywords that can be changed to the defaults dictionary 14.09.2019 diff --git a/camlib.py b/camlib.py index da42b5ca..14b2c978 100644 --- a/camlib.py +++ b/camlib.py @@ -5685,10 +5685,11 @@ class CNCjob(Geometry): must_visit.remove(nearest) return path - def generate_from_excellon_by_tool(self, exobj, tools="all", drillz = 3.0, - toolchange=False, toolchangez=0.1, toolchangexy='', - endz=2.0, startz=None, - excellon_optimization_type='B'): + def generate_from_excellon_by_tool( + self, exobj, tools="all", drillz = 3.0, + toolchange=False, toolchangez=0.1, toolchangexy='', + endz=2.0, startz=None, + excellon_optimization_type='B'): """ Creates gcode for this object from an Excellon object for the specified tools. @@ -6482,11 +6483,18 @@ class CNCjob(Geometry): # self.gcode += self.doformat(p.toolchange_code) self.gcode += self.doformat(p.toolchange_code) - self.gcode += self.doformat(p.spindle_code) # Spindle start + if 'laser' not in self.pp_geometry_name: + self.gcode += self.doformat(p.spindle_code) # Spindle start + else: + # for laser this will disable the laser + self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height + if self.dwell is True: self.gcode += self.doformat(p.dwell_code) # Dwell time else: - self.gcode += self.doformat(p.spindle_code) # Spindle start + if 'laser' not in self.pp_geometry_name: + self.gcode += self.doformat(p.spindle_code) # Spindle start + if self.dwell is True: self.gcode += self.doformat(p.dwell_code) # Dwell time @@ -6589,15 +6597,16 @@ class CNCjob(Geometry): ) return self.gcode - def generate_from_geometry_2(self, geometry, append=True, - tooldia=None, offset=0.0, tolerance=0, - z_cut=1.0, z_move=2.0, - feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30, - spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0, - multidepth=False, depthpercut=None, - toolchange=False, toolchangez=1.0, toolchangexy="0.0, 0.0", - extracut=False, startz=None, endz=2.0, - pp_geometry_name=None, tool_no=1): + def generate_from_geometry_2( + self, geometry, append=True, + tooldia=None, offset=0.0, tolerance=0, + z_cut=1.0, z_move=2.0, + feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30, + spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0, + multidepth=False, depthpercut=None, + toolchange=False, toolchangez=1.0, toolchangexy="0.0, 0.0", + extracut=False, startz=None, endz=2.0, + pp_geometry_name=None, tool_no=1): """ Second algorithm to generate from Geometry. @@ -6825,13 +6834,18 @@ class CNCjob(Geometry): # self.gcode += self.doformat(p.toolchange_code) self.gcode += self.doformat(p.toolchange_code) - self.gcode += self.doformat(p.spindle_code) # Spindle start + if 'laser' not in self.pp_geometry_name: + self.gcode += self.doformat(p.spindle_code) # Spindle start + else: + # for laser this will disable the laser + self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height if self.dwell is True: self.gcode += self.doformat(p.dwell_code) # Dwell time - else: - self.gcode += self.doformat(p.spindle_code) # Spindle start + if 'laser' not in self.pp_geometry_name: + self.gcode += self.doformat(p.spindle_code) # Spindle start + if self.dwell is True: self.gcode += self.doformat(p.dwell_code) # Dwell time @@ -7247,7 +7261,7 @@ class CNCjob(Geometry): match_lsr_pos = re.search(r"^(M0[3|5])", gline) if match_lsr_pos: - if match_lsr_pos.group(1) == 'M05': + if 'M05' in match_lsr_pos.group(1): # the value does not matter, only that it is positive so the gcode_parse() know it is > 0, # therefore the move is of kind T (travel) command['Z'] = 1 @@ -7316,7 +7330,7 @@ class CNCjob(Geometry): pass elif 'hpgl' in self.pp_excellon_name or 'hpgl' in self.pp_geometry_name: pass - elif 'grbl_laser' in self.pp_excellon_name or 'grbl_laser' in self.pp_geometry_name: + elif 'laser' in self.pp_excellon_name or 'laser' in self.pp_geometry_name: pass elif ('X' in gobj or 'Y' in gobj) and gobj['Z'] != current['Z']: if self.pp_geometry_name == 'line_xyz' or self.pp_excellon_name == 'line_xyz': diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 126a9dcf..ee82a305 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -400,6 +400,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), _('Online Help\tF1')) self.menuhelp_home = self.menuhelp.addAction(QtGui.QIcon('share/home16.png'), _('FlatCAM.org')) self.menuhelp.addSeparator() + self.menuhelp_report_bug = self.menuhelp.addAction(QtGui.QIcon('share/bug16.png'), _('Report a bug')) + self.menuhelp.addSeparator() + self.menuhelp_exc_spec = self.menuhelp.addAction(QtGui.QIcon('share/pdf_link16.png'), + _('Excellon Specification')) + self.menuhelp_gerber_spec = self.menuhelp.addAction(QtGui.QIcon('share/pdf_link16.png'), + _('Gerber Specification')) + + self.menuhelp.addSeparator() + self.menuhelp_shortcut_list = self.menuhelp.addAction(QtGui.QIcon('share/shortcuts24.png'), _('Shortcuts List\tF3')) self.menuhelp_videohelp = self.menuhelp.addAction(QtGui.QIcon('share/youtube32.png'), _('YouTube Channel\tF4') @@ -2044,7 +2053,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): def eventFilter(self, obj, event): # filter the ToolTips display based on a Preferences setting - if self.general_defaults_form.general_app_group.toggle_tooltips_cb.get_value() is False: + if self.general_defaults_form.general_gui_set_group.toggle_tooltips_cb.get_value() is False: if event.type() == QtCore.QEvent.ToolTip: return True else: @@ -4026,6 +4035,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI): ) self.selection_cb = FCCheckBox() + # Notebook Font Size self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('NB Font Size')) self.notebook_font_size_label.setToolTip( _("This sets the font size for the elements found in the Notebook.\n" @@ -4074,6 +4084,56 @@ class GeneralGUISetGroupUI(OptionsGroupUI): else: self.splash_cb.set_value(False) + # Shell StartUp CB + self.shell_startup_label = QtWidgets.QLabel('%s:' % _('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.") + ) + + # Project at StartUp CB + self.project_startup_label = QtWidgets.QLabel('%s:' % _('Project at StartUp')) + self.project_startup_label.setToolTip( + _("Check this box if you want the project/selected/tool tab area to\n" + "to be shown automatically at startup.") + ) + self.project_startup_cb = FCCheckBox(label='') + self.project_startup_cb.setToolTip( + _("Check this box if you want the project/selected/tool tab area to\n" + "to be shown automatically at startup.") + ) + + # Project autohide CB + self.project_autohide_label = QtWidgets.QLabel('%s:' % _('Project AutoHide')) + self.project_autohide_label.setToolTip( + _("Check this box if you want the project/selected/tool tab area to\n" + "hide automatically when there are no objects loaded and\n" + "to show whenever a new object is created.") + ) + self.project_autohide_cb = FCCheckBox(label='') + self.project_autohide_cb.setToolTip( + _("Check this box if you want the project/selected/tool tab area to\n" + "hide automatically when there are no objects loaded and\n" + "to show whenever a new object is created.") + ) + + # Enable/Disable ToolTips globally + self.toggle_tooltips_label = QtWidgets.QLabel('%s:' % _('Enable ToolTips')) + self.toggle_tooltips_label.setToolTip( + _("Check this box if you want to have toolTips displayed\n" + "when hovering with mouse over items throughout the App.") + ) + self.toggle_tooltips_cb = FCCheckBox(label='') + self.toggle_tooltips_cb.setToolTip( + _("Check this box if you want to have toolTips displayed\n" + "when hovering with mouse over items throughout the App.") + ) + # Add (label - input field) pair to the QFormLayout self.form_box.addRow(self.spacelabel, self.spacelabel) @@ -4088,11 +4148,27 @@ class GeneralGUISetGroupUI(OptionsGroupUI): self.form_box.addRow(self.axis_font_size_label, self.axis_font_size_spinner) self.form_box.addRow(QtWidgets.QLabel('')) self.form_box.addRow(self.splash_label, self.splash_cb) + self.form_box.addRow(self.shell_startup_label, self.shell_startup_cb) + self.form_box.addRow(self.project_startup_label, self.project_startup_cb) + self.form_box.addRow(self.project_autohide_label, self.project_autohide_cb) + self.form_box.addRow(QtWidgets.QLabel('')) + self.form_box.addRow(self.toggle_tooltips_label, self.toggle_tooltips_cb) # Add the QFormLayout that holds the Application general defaults # to the main layout of this TAB self.layout.addLayout(self.form_box) + # Delete confirmation + self.delete_conf_cb = FCCheckBox(_('Delete object confirmation')) + self.delete_conf_cb.setToolTip( + _("When checked the application will ask for user confirmation\n" + "whenever the Delete object(s) event is triggered, either by\n" + "menu shortcut or key shortcut.") + ) + self.layout.addWidget(self.delete_conf_cb) + + self.layout.addStretch() + def handle_style(self, style): # set current style settings = QSettings("Open Source", "FlatCAM") @@ -4141,7 +4217,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): self.form_box = QtWidgets.QFormLayout() # Units for FlatCAM - self.unitslabel = QtWidgets.QLabel('%s:' % _('Units')) + self.unitslabel = QtWidgets.QLabel('%s:' % _('Units')) self.unitslabel.setToolTip(_("The default value for FlatCAM units.\n" "Whatever is selected here is set every time\n" "FLatCAM is started.")) @@ -4180,18 +4256,6 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): "security features. In this case the language will be\n" "applied at the next app start.")) - # Shell StartUp CB - self.shell_startup_label = QtWidgets.QLabel('%s:' % _('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('%s:' % _('Version Check')) self.version_check_label.setToolTip( @@ -4232,43 +4296,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): self.mselect_radio = RadioSet([{'label': _('CTRL'), 'value': 'Control'}, {'label': _('SHIFT'), 'value': 'Shift'}]) - # Project at StartUp CB - self.project_startup_label = QtWidgets.QLabel('%s:' % _('Project at StartUp')) - self.project_startup_label.setToolTip( - _("Check this box if you want the project/selected/tool tab area to\n" - "to be shown automatically at startup.") - ) - self.project_startup_cb = FCCheckBox(label='') - self.project_startup_cb.setToolTip( - _("Check this box if you want the project/selected/tool tab area to\n" - "to be shown automatically at startup.") - ) - - # Project autohide CB - self.project_autohide_label = QtWidgets.QLabel('%s:' % _('Project AutoHide')) - self.project_autohide_label.setToolTip( - _("Check this box if you want the project/selected/tool tab area to\n" - "hide automatically when there are no objects loaded and\n" - "to show whenever a new object is created.") - ) - self.project_autohide_cb = FCCheckBox(label='') - self.project_autohide_cb.setToolTip( - _("Check this box if you want the project/selected/tool tab area to\n" - "hide automatically when there are no objects loaded and\n" - "to show whenever a new object is created.") - ) - - # Enable/Disable ToolTips globally - self.toggle_tooltips_label = QtWidgets.QLabel('%s:' % _('Enable ToolTips')) - self.toggle_tooltips_label.setToolTip( - _("Check this box if you want to have toolTips displayed\n" - "when hovering with mouse over items throughout the App.") - ) - self.toggle_tooltips_cb = FCCheckBox(label='') - self.toggle_tooltips_cb.setToolTip( - _("Check this box if you want to have toolTips displayed\n" - "when hovering with mouse over items throughout the App.") - ) + # Worker Numbers self.worker_number_label = QtWidgets.QLabel('%s:' % _('Workers number')) self.worker_number_label.setToolTip( _("The number of Qthreads made available to the App.\n" @@ -4321,15 +4349,11 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): 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.project_startup_label, self.project_startup_cb) - self.form_box.addRow(self.project_autohide_label, self.project_autohide_cb) - self.form_box.addRow(self.toggle_tooltips_label, self.toggle_tooltips_cb) self.form_box.addRow(self.worker_number_label, self.worker_number_sb) self.form_box.addRow(tol_label, self.tol_entry) @@ -4350,15 +4374,6 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): # self.advanced_cb.setLayoutDirection(QtCore.Qt.RightToLeft) self.layout.addWidget(self.open_style_cb) - # Delete confirmation - self.delete_conf_cb = FCCheckBox(_('Delete object confirmation')) - self.delete_conf_cb.setToolTip( - _("When checked the application will ask for user confirmation\n" - "whenever the Delete object(s) event is triggered, either by\n" - "menu shortcut or key shortcut.") - ) - self.layout.addWidget(self.delete_conf_cb) - # Save compressed project CB self.save_type_cb = FCCheckBox(_('Save Compressed Project')) self.save_type_cb.setToolTip( @@ -4442,40 +4457,6 @@ class GerberGenPrefGroupUI(OptionsGroupUI): grid0.addWidget(self.circle_steps_label, 1, 0) grid0.addWidget(self.circle_steps_entry, 1, 1) - # Milling Type - buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) - buffering_label.setToolTip( - _("Buffering type:\n" - "- None --> best performance, fast file loading but no so good display\n" - "- Full --> slow file loading but good visuals. This is the default.\n" - "<>: Don't change this unless you know what you are doing !!!") - ) - self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'}, - {'label': _('Full'), 'value': 'full'}]) - grid0.addWidget(buffering_label, 2, 0) - grid0.addWidget(self.buffering_radio, 2, 1) - - # Simplification - self.simplify_cb = FCCheckBox(label=_('Simplify')) - self.simplify_cb.setToolTip(_("When checked all the Gerber polygons will be\n" - "loaded with simplification having a set tolerance.")) - grid0.addWidget(self.simplify_cb, 3, 0) - - # Simplification tolerance - self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance')) - self.simplification_tol_label.setToolTip(_("Tolerance for poligon simplification.")) - - self.simplification_tol_spinner = FCDoubleSpinner() - self.simplification_tol_spinner.set_precision(5) - self.simplification_tol_spinner.setWrapping(True) - self.simplification_tol_spinner.setRange(0.00000, 0.01000) - self.simplification_tol_spinner.setSingleStep(0.0001) - - grid0.addWidget(self.simplification_tol_label, 4, 0) - grid0.addWidget(self.simplification_tol_spinner, 4, 1) - self.ois_simplif = OptionalInputSection(self.simplify_cb, - [self.simplification_tol_label, self.simplification_tol_spinner], - logic=True) self.layout.addStretch() @@ -4525,7 +4506,11 @@ class GerberOptPrefGroupUI(OptionsGroupUI): "A value here of 0.25 means an overlap of 25%% from the tool diameter found above.") ) grid0.addWidget(overlabel, 2, 0) - self.iso_overlap_entry = FloatEntry() + self.iso_overlap_entry = FCDoubleSpinner() + self.iso_overlap_entry.set_precision(3) + self.iso_overlap_entry.setWrapping(True) + self.iso_overlap_entry.setRange(0.000, 0.999) + self.iso_overlap_entry.setSingleStep(0.1) grid0.addWidget(self.iso_overlap_entry, 2, 1) # Milling Type @@ -4615,7 +4600,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.setTitle(str(_("Gerber Adv. Options"))) # ## Advanced Gerber Parameters - self.adv_param_label = QtWidgets.QLabel("%s:" % _("Advanced Param.")) + self.adv_param_label = QtWidgets.QLabel('%s:' % _('Advanced Options')) self.adv_param_label.setToolTip( _("A list of Gerber advanced parameters.\n" "Those parameters are available only for\n" @@ -4633,7 +4618,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): "This means that it will cut through\n" "the middle of the trace.") ) - grid0.addWidget(self.follow_cb, 0, 0) + grid0.addWidget(self.follow_cb, 0, 0, 1, 2) # Aperture Table Visibility CB self.aperture_table_visibility_cb = FCCheckBox(label=_('Table Show/Hide')) @@ -4643,7 +4628,42 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): "that are drawn on canvas.") ) - grid0.addWidget(self.aperture_table_visibility_cb, 1, 0) + grid0.addWidget(self.aperture_table_visibility_cb, 1, 0, 1, 2) + + # Buffering Type + buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) + buffering_label.setToolTip( + _("Buffering type:\n" + "- None --> best performance, fast file loading but no so good display\n" + "- Full --> slow file loading but good visuals. This is the default.\n" + "<>: Don't change this unless you know what you are doing !!!") + ) + self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'}, + {'label': _('Full'), 'value': 'full'}]) + grid0.addWidget(buffering_label, 2, 0) + grid0.addWidget(self.buffering_radio, 2, 1) + + # Simplification + self.simplify_cb = FCCheckBox(label=_('Simplify')) + self.simplify_cb.setToolTip(_("When checked all the Gerber polygons will be\n" + "loaded with simplification having a set tolerance.")) + grid0.addWidget(self.simplify_cb, 3, 0, 1, 2) + + # Simplification tolerance + self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance')) + self.simplification_tol_label.setToolTip(_("Tolerance for poligon simplification.")) + + self.simplification_tol_spinner = FCDoubleSpinner() + self.simplification_tol_spinner.set_precision(5) + self.simplification_tol_spinner.setWrapping(True) + self.simplification_tol_spinner.setRange(0.00000, 0.01000) + self.simplification_tol_spinner.setSingleStep(0.0001) + + grid0.addWidget(self.simplification_tol_label, 4, 0) + grid0.addWidget(self.simplification_tol_spinner, 4, 1) + self.ois_simplif = OptionalInputSection(self.simplify_cb, + [self.simplification_tol_label, self.simplification_tol_spinner], + logic=True) # Scale Aperture Factor # self.scale_aperture_label = QtWidgets.QLabel(_('Ap. Scale Factor:')) @@ -5141,7 +5161,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): self.update_excellon_cb.setToolTip( "If checked, the Excellon Export settings will be updated with the ones above." ) - grid2.addWidget(self.update_excellon_cb, 2, 0) + grid2.addWidget(self.update_excellon_cb, 2, 0, 1, 2) grid2.addWidget(QtWidgets.QLabel(""), 3, 0) @@ -5161,8 +5181,10 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): ) grid2.addWidget(self.excellon_optimization_label, 5, 0) - self.excellon_optimization_radio = RadioSet([{'label': _('MH'), 'value': 'M'}, - {'label': _('Basic'), 'value': 'B'}]) + self.excellon_optimization_radio = RadioSet([{'label': _('MetaHeuristic'), 'value': 'M'}, + {'label': _('Basic'), 'value': 'B'}, + {'label': _('TSA'), 'value': 'T'}], + orientation='vertical', stretch=False) self.excellon_optimization_radio.setToolTip( _("This sets the optimization type for the Excellon drill path.\n" "If MH is checked then Google OR-Tools algorithm with MetaHeuristic\n" @@ -5234,6 +5256,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): grid2 = QtWidgets.QGridLayout() self.layout.addLayout(grid2) + # Cut Z cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) cutzlabel.setToolTip( _("Drill depth (negative)\n" @@ -5243,6 +5266,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): self.cutz_entry = LengthEntry() grid2.addWidget(self.cutz_entry, 0, 1) + # Travel Z travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z')) travelzlabel.setToolTip( _("Tool height when travelling\n" @@ -5271,15 +5295,27 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): self.toolchangez_entry = LengthEntry() grid2.addWidget(self.toolchangez_entry, 3, 1) - frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge)')) + # End Move Z + endzlabel = QtWidgets.QLabel('%s:' % _('End move Z')) + endzlabel.setToolTip( + _("Height of the tool after\n" + "the last move at the end of the job.") + ) + grid2.addWidget(endzlabel, 4, 0) + self.eendz_entry = LengthEntry() + grid2.addWidget(self.eendz_entry, 4, 1) + + # Feedrate Z + frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z')) frlabel.setToolTip( _("Tool speed while drilling\n" "(in units per minute).\n" + "So called 'Plunge' feedrate.\n" "This is for linear move G01.") ) - grid2.addWidget(frlabel, 4, 0) + grid2.addWidget(frlabel, 5, 0) self.feedrate_entry = LengthEntry() - grid2.addWidget(self.feedrate_entry, 4, 1) + grid2.addWidget(self.feedrate_entry, 5, 1) # Spindle speed spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed')) @@ -5287,23 +5323,9 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): _("Speed of the spindle\n" "in RPM (optional)") ) - grid2.addWidget(spdlabel, 5, 0) + grid2.addWidget(spdlabel, 6, 0) self.spindlespeed_entry = IntEntry(allow_empty=True) - grid2.addWidget(self.spindlespeed_entry, 5, 1) - - # Spindle direction - spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle dir.')) - spindle_dir_label.setToolTip( - _("This sets the direction that the spindle is rotating.\n" - "It can be either:\n" - "- CW = clockwise or\n" - "- CCW = counter clockwise") - ) - - self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'}, - {'label': _('CCW'), 'value': 'CCW'}]) - grid2.addWidget(spindle_dir_label, 6, 0) - grid2.addWidget(self.spindledir_radio, 6, 1) + grid2.addWidget(self.spindlespeed_entry, 6, 1) # Dwell dwelllabel = QtWidgets.QLabel('%s:' % _('Dwell')) @@ -5317,6 +5339,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): ) self.dwell_cb = FCCheckBox() self.dwelltime_entry = FCEntry() + grid2.addWidget(dwelllabel, 7, 0) grid2.addWidget(self.dwell_cb, 7, 1) grid2.addWidget(dwelltime, 8, 0) @@ -5358,7 +5381,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): self.mill_hole_label.setToolTip( _("Create Geometry for milling holes.") ) - grid2.addWidget(excellon_gcode_type_label, 11, 0, 1, 2) + grid2.addWidget(self.mill_hole_label, 11, 0, 1, 2) tdlabel = QtWidgets.QLabel('%s:' % _('Drill Tool dia')) tdlabel.setToolTip( @@ -5400,12 +5423,13 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): # ## ADVANCED OPTIONS ### # ####################### - self.cncjob_label = QtWidgets.QLabel('%s:' % _('Advanced Options')) - self.cncjob_label.setToolTip( - _("Parameters used to create a CNC Job object\n" - "for this drill object that are shown when App Level is Advanced.") + self.exc_label = QtWidgets.QLabel('%s:' % _('Advanced Options')) + self.exc_label.setToolTip( + _("A list of Excellon advanced parameters.\n" + "Those parameters are available only for\n" + "Advanced App. Level.") ) - self.layout.addWidget(self.cncjob_label) + self.layout.addWidget(self.exc_label) grid1 = QtWidgets.QGridLayout() self.layout.addLayout(grid1) @@ -5436,15 +5460,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): self.estartz_entry = FloatEntry() grid1.addWidget(self.estartz_entry, 2, 1) - endzlabel = QtWidgets.QLabel('%s:' % _('End move Z')) - endzlabel.setToolTip( - _("Height of the tool after\n" - "the last move at the end of the job.") - ) - grid1.addWidget(endzlabel, 3, 0) - self.eendz_entry = LengthEntry() - grid1.addWidget(self.eendz_entry, 3, 1) - + # Feedrate Rapids fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids')) fr_rapid_label.setToolTip( _("Tool speed while drilling\n" @@ -5453,9 +5469,9 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): "It is useful only for Marlin,\n" "ignore for any other cases.") ) - grid1.addWidget(fr_rapid_label, 4, 0) + grid1.addWidget(fr_rapid_label, 3, 0) self.feedrate_rapid_entry = LengthEntry() - grid1.addWidget(self.feedrate_rapid_entry, 4, 1) + grid1.addWidget(self.feedrate_rapid_entry, 3, 1) # Probe depth self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth")) @@ -5463,18 +5479,32 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): _("The maximum depth that the probe is allowed\n" "to probe. Negative value, in current units.") ) - grid1.addWidget(self.pdepth_label, 5, 0) + grid1.addWidget(self.pdepth_label, 4, 0) self.pdepth_entry = FCEntry() - grid1.addWidget(self.pdepth_entry, 5, 1) + grid1.addWidget(self.pdepth_entry, 4, 1) # Probe feedrate self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe")) self.feedrate_probe_label.setToolTip( _("The feedrate used while the probe is probing.") ) - grid1.addWidget(self.feedrate_probe_label, 6, 0) + grid1.addWidget(self.feedrate_probe_label, 5, 0) self.feedrate_probe_entry = FCEntry() - grid1.addWidget(self.feedrate_probe_entry, 6, 1) + grid1.addWidget(self.feedrate_probe_entry, 5, 1) + + # Spindle direction + spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle dir.')) + spindle_dir_label.setToolTip( + _("This sets the direction that the spindle is rotating.\n" + "It can be either:\n" + "- CW = clockwise or\n" + "- CCW = counter clockwise") + ) + + self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'}, + {'label': _('CCW'), 'value': 'CCW'}]) + grid1.addWidget(spindle_dir_label, 6, 0) + grid1.addWidget(self.spindledir_radio, 6, 1) fplungelabel = QtWidgets.QLabel('%s:' % _('Fast Plunge')) fplungelabel.setToolTip( @@ -6048,15 +6078,25 @@ class GeometryOptPrefGroupUI(OptionsGroupUI): self.toolchangez_entry = LengthEntry() grid1.addWidget(self.toolchangez_entry, 5, 1) + # End move Z + endzlabel = QtWidgets.QLabel('%s:' % _('End move Z')) + endzlabel.setToolTip( + _("Height of the tool after\n" + "the last move at the end of the job.") + ) + grid1.addWidget(endzlabel, 6, 0) + self.gendz_entry = LengthEntry() + grid1.addWidget(self.gendz_entry, 6, 1) + # Feedrate X-Y frlabel = QtWidgets.QLabel('%s:' % _('Feed Rate X-Y')) frlabel.setToolTip( _("Cutting speed in the XY\n" "plane in units per minute") ) - grid1.addWidget(frlabel, 6, 0) + grid1.addWidget(frlabel, 7, 0) self.cncfeedrate_entry = LengthEntry() - grid1.addWidget(self.cncfeedrate_entry, 6, 1) + grid1.addWidget(self.cncfeedrate_entry, 7, 1) # Feedrate Z (Plunge) frz_label = QtWidgets.QLabel('%s:' % _('Feed Rate Z')) @@ -6065,9 +6105,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI): "plane in units per minute.\n" "It is called also Plunge.") ) - grid1.addWidget(frz_label, 7, 0) + grid1.addWidget(frz_label, 8, 0) self.cncplunge_entry = LengthEntry() - grid1.addWidget(self.cncplunge_entry, 7, 1) + grid1.addWidget(self.cncplunge_entry, 8, 1) # Spindle Speed spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed')) @@ -6078,23 +6118,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI): "this value is the power of laser." ) ) - grid1.addWidget(spdlabel, 8, 0) + grid1.addWidget(spdlabel, 9, 0) self.cncspindlespeed_entry = IntEntry(allow_empty=True) - grid1.addWidget(self.cncspindlespeed_entry, 8, 1) - - # Spindle direction - spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle dir.')) - spindle_dir_label.setToolTip( - _("This sets the direction that the spindle is rotating.\n" - "It can be either:\n" - "- CW = clockwise or\n" - "- CCW = counter clockwise") - ) - - self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'}, - {'label': _('CCW'), 'value': 'CCW'}]) - grid1.addWidget(spindle_dir_label, 9, 0) - grid1.addWidget(self.spindledir_radio, 9, 1) + grid1.addWidget(self.cncspindlespeed_entry, 9, 1) # Dwell self.dwell_cb = FCCheckBox(label='%s:' % _('Dwell')) @@ -6137,12 +6163,13 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): # ------------------------------ # ## Advanced Options # ------------------------------ - self.cncjob_label = QtWidgets.QLabel('%s:' % _('Advanced Options')) - self.cncjob_label.setToolTip( - _("Parameters to create a CNC Job object\n" - "tracing the contours of a Geometry object.") + self.geo_label = QtWidgets.QLabel('%s:' % _('Advanced Options')) + self.geo_label.setToolTip( + _("A list of Geometry advanced parameters.\n" + "Those parameters are available only for\n" + "Advanced App. Level.") ) - self.layout.addWidget(self.cncjob_label) + self.layout.addWidget(self.geo_label) grid1 = QtWidgets.QGridLayout() self.layout.addLayout(grid1) @@ -6166,16 +6193,6 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): self.gstartz_entry = FloatEntry() grid1.addWidget(self.gstartz_entry, 2, 1) - # End move Z - endzlabel = QtWidgets.QLabel('%s:' % _('End move Z')) - endzlabel.setToolTip( - _("Height of the tool after\n" - "the last move at the end of the job.") - ) - grid1.addWidget(endzlabel, 3, 0) - self.gendz_entry = LengthEntry() - grid1.addWidget(self.gendz_entry, 3, 1) - # Feedrate rapids fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feed Rate Rapids')) fr_rapid_label.setToolTip( @@ -6218,6 +6235,20 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): self.feedrate_probe_entry = FCEntry() grid1.addWidget(self.feedrate_probe_entry, 7, 1) + # Spindle direction + spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle dir.')) + spindle_dir_label.setToolTip( + _("This sets the direction that the spindle is rotating.\n" + "It can be either:\n" + "- CW = clockwise or\n" + "- CCW = counter clockwise") + ) + + self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'}, + {'label': _('CCW'), 'value': 'CCW'}]) + grid1.addWidget(spindle_dir_label, 8, 0) + grid1.addWidget(self.spindledir_radio, 8, 1) + # Fast Move from Z Toolchange fplungelabel = QtWidgets.QLabel('%s:' % _('Fast Plunge')) fplungelabel.setToolTip( @@ -6227,8 +6258,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "WARNING: the move is done at Toolchange X,Y coords.") ) self.fplunge_cb = FCCheckBox() - grid1.addWidget(fplungelabel, 8, 0) - grid1.addWidget(self.fplunge_cb, 8, 1) + grid1.addWidget(fplungelabel, 9, 0) + grid1.addWidget(self.fplunge_cb, 9, 1) # Size of trace segment on X axis segx_label = QtWidgets.QLabel('%s:' % _("Seg. X size")) @@ -6237,9 +6268,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "Useful for auto-leveling.\n" "A value of 0 means no segmentation on the X axis.") ) - grid1.addWidget(segx_label, 9, 0) + grid1.addWidget(segx_label, 10, 0) self.segx_entry = FCEntry() - grid1.addWidget(self.segx_entry, 9, 1) + grid1.addWidget(self.segx_entry, 10, 1) # Size of trace segment on Y axis segy_label = QtWidgets.QLabel('%s:' % _("Seg. Y size")) @@ -6248,9 +6279,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "Useful for auto-leveling.\n" "A value of 0 means no segmentation on the Y axis.") ) - grid1.addWidget(segy_label, 10, 0) + grid1.addWidget(segy_label, 11, 0) self.segy_entry = FCEntry() - grid1.addWidget(self.segy_entry, 10, 1) + grid1.addWidget(self.segy_entry, 11, 1) self.layout.addStretch() @@ -6344,37 +6375,6 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): grid0.addWidget(self.annotation_cb, 2, 1) grid0.addWidget(QtWidgets.QLabel(''), 2, 2) - # Annotation Font Size - self.annotation_fontsize_label = QtWidgets.QLabel('%s:' % _("Annotation Size")) - self.annotation_fontsize_label.setToolTip( - _("The font size of the annotation text. In pixels.") - ) - grid0.addWidget(self.annotation_fontsize_label, 3, 0) - self.annotation_fontsize_sp = FCSpinner() - grid0.addWidget(self.annotation_fontsize_sp, 3, 1) - grid0.addWidget(QtWidgets.QLabel(''), 3, 2) - - # Annotation Font Color - self.annotation_color_label = QtWidgets.QLabel('%s:' % _('Annotation Color')) - self.annotation_color_label.setToolTip( - _("Set the font color for the annotation texts.") - ) - self.annotation_fontcolor_entry = FCEntry() - self.annotation_fontcolor_button = QtWidgets.QPushButton() - self.annotation_fontcolor_button.setFixedSize(15, 15) - - self.form_box_child = QtWidgets.QHBoxLayout() - self.form_box_child.setContentsMargins(0, 0, 0, 0) - self.form_box_child.addWidget(self.annotation_fontcolor_entry) - self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight) - self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) - - color_widget = QtWidgets.QWidget() - color_widget.setLayout(self.form_box_child) - grid0.addWidget(self.annotation_color_label, 4, 0) - grid0.addWidget(color_widget, 4, 1) - grid0.addWidget(QtWidgets.QLabel(''), 4, 2) - # ################################################################### # Number of circle steps for circular aperture linear approximation # # ################################################################### @@ -6383,9 +6383,9 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): _("The number of circle steps for GCode \n" "circle and arc shapes linear approximation.") ) - grid0.addWidget(self.steps_per_circle_label, 5, 0) + grid0.addWidget(self.steps_per_circle_label, 3, 0) self.steps_per_circle_entry = IntEntry() - grid0.addWidget(self.steps_per_circle_entry, 5, 1) + grid0.addWidget(self.steps_per_circle_entry, 3, 1) # Tool dia for plot tdlabel = QtWidgets.QLabel('%s:' % _('Travel dia')) @@ -6394,11 +6394,11 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): "rendered in the plot.") ) self.tooldia_entry = LengthEntry() - grid0.addWidget(tdlabel, 6, 0) - grid0.addWidget(self.tooldia_entry, 6, 1) + grid0.addWidget(tdlabel, 4, 0) + grid0.addWidget(self.tooldia_entry, 4, 1) # add a space - grid0.addWidget(QtWidgets.QLabel(''), 7, 0) + grid0.addWidget(QtWidgets.QLabel(''), 5, 0) # Number of decimals to use in GCODE coordinates cdeclabel = QtWidgets.QLabel('%s:' % _('Coordinates decimals')) @@ -6407,8 +6407,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): "the X, Y, Z coordinates in CNC code (GCODE, etc.)") ) self.coords_dec_entry = IntEntry() - grid0.addWidget(cdeclabel, 8, 0) - grid0.addWidget(self.coords_dec_entry, 8, 1) + grid0.addWidget(cdeclabel, 6, 0) + grid0.addWidget(self.coords_dec_entry, 6, 1) # Number of decimals to use in GCODE feedrate frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate decimals')) @@ -6417,8 +6417,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): "the Feedrate parameter in CNC code (GCODE, etc.)") ) self.fr_dec_entry = IntEntry() - grid0.addWidget(frdeclabel, 9, 0) - grid0.addWidget(self.fr_dec_entry, 9, 1) + grid0.addWidget(frdeclabel, 7, 0) + grid0.addWidget(self.fr_dec_entry, 7, 1) # The type of coordinates used in the Gcode: Absolute or Incremental coords_type_label = QtWidgets.QLabel('%s:' % _('Coordinates type')) @@ -6432,8 +6432,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): {"label": _("Absolute G90"), "value": "G90"}, {"label": _("Incremental G91"), "value": "G91"} ], orientation='vertical', stretch=False) - grid0.addWidget(coords_type_label, 10, 0) - grid0.addWidget(self.coords_type_radio, 10, 1) + grid0.addWidget(coords_type_label, 8, 0) + grid0.addWidget(self.coords_type_radio, 8, 1) # hidden for the time being, until implemented coords_type_label.hide() @@ -6570,6 +6570,42 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI): # ) # hlay1.addWidget(self.tc_insert_buton) + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + grid0.addWidget(QtWidgets.QLabel(''), 1, 0, 1, 2) + + # Annotation Font Size + self.annotation_fontsize_label = QtWidgets.QLabel('%s:' % _("Annotation Size")) + self.annotation_fontsize_label.setToolTip( + _("The font size of the annotation text. In pixels.") + ) + grid0.addWidget(self.annotation_fontsize_label, 2, 0) + self.annotation_fontsize_sp = FCSpinner() + grid0.addWidget(self.annotation_fontsize_sp, 2, 1) + grid0.addWidget(QtWidgets.QLabel(''), 2, 2) + + # Annotation Font Color + self.annotation_color_label = QtWidgets.QLabel('%s:' % _('Annotation Color')) + self.annotation_color_label.setToolTip( + _("Set the font color for the annotation texts.") + ) + self.annotation_fontcolor_entry = FCEntry() + self.annotation_fontcolor_button = QtWidgets.QPushButton() + self.annotation_fontcolor_button.setFixedSize(15, 15) + + self.form_box_child = QtWidgets.QHBoxLayout() + self.form_box_child.setContentsMargins(0, 0, 0, 0) + self.form_box_child.addWidget(self.annotation_fontcolor_entry) + self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight) + self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + color_widget = QtWidgets.QWidget() + color_widget.setLayout(self.form_box_child) + grid0.addWidget(self.annotation_color_label, 3, 0) + grid0.addWidget(color_widget, 3, 1) + grid0.addWidget(QtWidgets.QLabel(''), 3, 2) + self.layout.addStretch() @@ -6705,8 +6741,12 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "Higher values = slow processing and slow execution on CNC\n" "due of too many paths.") ) + self.ncc_overlap_entry = FCDoubleSpinner() + self.ncc_overlap_entry.set_precision(3) + self.ncc_overlap_entry.setWrapping(True) + self.ncc_overlap_entry.setRange(0.000, 0.999) + self.ncc_overlap_entry.setSingleStep(0.1) grid0.addWidget(nccoverlabel, 7, 0) - self.ncc_overlap_entry = FloatEntry() grid0.addWidget(self.ncc_overlap_entry, 7, 1) # Margin entry @@ -7050,8 +7090,12 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): "Higher values = slow processing and slow execution on CNC\n" "due of too many paths.") ) + self.paintoverlap_entry = FCDoubleSpinner() + self.paintoverlap_entry.set_precision(3) + self.paintoverlap_entry.setWrapping(True) + self.paintoverlap_entry.setRange(0.000, 0.999) + self.paintoverlap_entry.setSingleStep(0.1) grid0.addWidget(ovlabel, 2, 0) - self.paintoverlap_entry = LengthEntry() grid0.addWidget(self.paintoverlap_entry, 2, 1) # Margin @@ -7167,6 +7211,26 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): grid0.addWidget(ftypelbl, 0, 0) grid0.addWidget(self.film_type_radio, 0, 1) + # Film Color + self.film_color_label = QtWidgets.QLabel('%s:' % _('Film Color')) + self.film_color_label.setToolTip( + _("Set the film color when positive film is selected.") + ) + self.film_color_entry = FCEntry() + self.film_color_button = QtWidgets.QPushButton() + self.film_color_button.setFixedSize(15, 15) + + self.form_box_child = QtWidgets.QHBoxLayout() + self.form_box_child.setContentsMargins(0, 0, 0, 0) + self.form_box_child.addWidget(self.film_color_entry) + self.form_box_child.addWidget(self.film_color_button, alignment=Qt.AlignRight) + self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + + film_color_widget = QtWidgets.QWidget() + film_color_widget.setLayout(self.form_box_child) + grid0.addWidget(self.film_color_label, 1, 0) + grid0.addWidget(film_color_widget, 1, 1) + self.film_boundary_entry = FCEntry() self.film_boundary_label = QtWidgets.QLabel('%s:' % _("Border")) self.film_boundary_label.setToolTip( @@ -7179,8 +7243,8 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): "white color like the rest and which may confound with the\n" "surroundings if not for this border.") ) - grid0.addWidget(self.film_boundary_label, 1, 0) - grid0.addWidget(self.film_boundary_entry, 1, 1) + grid0.addWidget(self.film_boundary_label, 2, 0) + grid0.addWidget(self.film_boundary_entry, 2, 1) self.film_scale_entry = FCEntry() self.film_scale_label = QtWidgets.QLabel('%s:' % _("Scale Stroke")) @@ -7189,8 +7253,8 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): "It means that the line that envelope each SVG feature will be thicker or thinner,\n" "therefore the fine features may be more affected by this parameter.") ) - grid0.addWidget(self.film_scale_label, 2, 0) - grid0.addWidget(self.film_scale_entry, 2, 1) + grid0.addWidget(self.film_scale_label, 3, 0) + grid0.addWidget(self.film_scale_entry, 3, 1) self.layout.addStretch() @@ -7737,7 +7801,7 @@ class FAExcPrefGroupUI(OptionsGroupUI): self.layout.addWidget(self.exc_list_label) self.exc_list_text = FCTextArea() - self.exc_list_text.sizeHint(custom_sizehint=150) + # self.exc_list_text.sizeHint(custom_sizehint=150) font = QtGui.QFont() font.setPointSize(12) self.exc_list_text.setFont(font) @@ -7770,7 +7834,7 @@ class FAGcoPrefGroupUI(OptionsGroupUI): self.layout.addWidget(self.gco_list_label) self.gco_list_text = FCTextArea() - self.gco_list_text.sizeHint(custom_sizehint=150) + # self.gco_list_text.sizeHint(custom_sizehint=150) font = QtGui.QFont() font.setPointSize(12) self.gco_list_text.setFont(font) @@ -7803,7 +7867,7 @@ class FAGrbPrefGroupUI(OptionsGroupUI): self.layout.addWidget(self.grb_list_label) self.grb_list_text = FCTextArea() - self.grb_list_text.sizeHint(custom_sizehint=150) + # self.grb_list_text.sizeHint(custom_sizehint=150) self.layout.addWidget(self.grb_list_text) font = QtGui.QFont() font.setPointSize(12) @@ -7814,6 +7878,7 @@ class FAGrbPrefGroupUI(OptionsGroupUI): "FlatCAM and the files with above extensions.\n" "They will be active after next logon.\n" "This work only in Windows.")) + self.layout.addWidget(self.grb_list_btn) # self.layout.addStretch() diff --git a/flatcamGUI/ObjectUI.py b/flatcamGUI/ObjectUI.py index f1d98791..5a8b1922 100644 --- a/flatcamGUI/ObjectUI.py +++ b/flatcamGUI/ObjectUI.py @@ -292,7 +292,11 @@ class GerberObjectUI(ObjectUI): "A value here of 0.25 means an overlap of 25%% from the tool diameter found above.") ) overlabel.setMinimumWidth(90) - self.iso_overlap_entry = FloatEntry() + self.iso_overlap_entry = FCDoubleSpinner() + self.iso_overlap_entry.set_precision(3) + self.iso_overlap_entry.setWrapping(True) + self.iso_overlap_entry.setRange(0.000, 0.999) + self.iso_overlap_entry.setSingleStep(0.1) grid1.addWidget(overlabel, 2, 0) grid1.addWidget(self.iso_overlap_entry, 2, 1, 1, 2) @@ -709,11 +713,12 @@ class ExcellonObjectUI(ObjectUI): self.eendz_entry = LengthEntry() grid1.addWidget(self.eendz_entry, 5, 1) - # Excellon Feedrate - frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge)')) + # Excellon Feedrate Z + frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z')) frlabel.setToolTip( _("Tool speed while drilling\n" "(in units per minute).\n" + "So called 'Plunge' feedrate.\n" "This is for linear move G01.") ) grid1.addWidget(frlabel, 6, 0) diff --git a/flatcamTools/ToolFilm.py b/flatcamTools/ToolFilm.py index 875d4586..dbd39bca 100644 --- a/flatcamTools/ToolFilm.py +++ b/flatcamTools/ToolFilm.py @@ -274,7 +274,7 @@ class Film(FlatCAMTool): self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export SVG positive cancelled.")) return else: - self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width) + self.app.export_svg_positive(name, boxname, filename, scale_factor=scale_stroke_width) else: try: filename, _f = QtWidgets.QFileDialog.getSaveFileName( diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index e79f73c2..ba00f92e 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -292,7 +292,11 @@ class NonCopperClear(FlatCAMTool, Gerber): "Higher values = slow processing and slow execution on CNC\n" "due of too many paths.") ) - self.ncc_overlap_entry = FCEntry() + self.ncc_overlap_entry = FCDoubleSpinner() + self.ncc_overlap_entry.set_precision(3) + self.ncc_overlap_entry.setWrapping(True) + self.ncc_overlap_entry.setRange(0.000, 0.999) + self.ncc_overlap_entry.setSingleStep(0.1) grid3.addWidget(nccoverlabel, 2, 0) grid3.addWidget(self.ncc_overlap_entry, 2, 1) @@ -1292,6 +1296,7 @@ class NonCopperClear(FlatCAMTool, Gerber): method=None, rest=None, tools_storage=None, + plot=True, run_threaded=True): """ Clear the excess copper from the entire object. @@ -2189,9 +2194,9 @@ class NonCopperClear(FlatCAMTool, Gerber): def job_thread(app_obj): try: if rest_machining_choice is True: - app_obj.new_object("geometry", name, gen_clear_area_rest) + app_obj.new_object("geometry", name, gen_clear_area_rest, plot=plot) else: - app_obj.new_object("geometry", name, gen_clear_area) + app_obj.new_object("geometry", name, gen_clear_area, plot=plot) except FlatCAMApp.GracefulException: proc.done() return diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 0991bbc5..e60c92e8 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -214,8 +214,12 @@ class ToolPaint(FlatCAMTool, Gerber): "Higher values = slow processing and slow execution on CNC\n" "due of too many paths.") ) + self.paintoverlap_entry = FCDoubleSpinner() + self.paintoverlap_entry.set_precision(3) + self.paintoverlap_entry.setWrapping(True) + self.paintoverlap_entry.setRange(0.000, 0.999) + self.paintoverlap_entry.setSingleStep(0.1) grid3.addWidget(ovlabel, 1, 0) - self.paintoverlap_entry = FCEntry() grid3.addWidget(self.paintoverlap_entry, 1, 1) # Margin @@ -1168,7 +1172,9 @@ class ToolPaint(FlatCAMTool, Gerber): outname=None, connect=None, contour=None, - tools_storage=None): + tools_storage=None, + plot=True, + run_threaded=True): """ Paints a polygon selected by clicking on its interior or by having a point coordinates given @@ -1432,7 +1438,7 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: - app_obj.new_object("geometry", name, gen_paintarea) + app_obj.new_object("geometry", name, gen_paintarea, plot=plot) except FlatCAMApp.GracefulException: proc.done() return @@ -1451,8 +1457,11 @@ class ToolPaint(FlatCAMTool, Gerber): # Promise object with the new name self.app.collection.promise(name) - # Background - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + if run_threaded: + # Background + self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + else: + job_thread(app_obj=self.app) def paint_poly_all(self, obj, tooldia=None, @@ -1463,7 +1472,9 @@ class ToolPaint(FlatCAMTool, Gerber): outname=None, connect=None, contour=None, - tools_storage=None): + tools_storage=None, + plot=True, + run_threaded=True): """ Paints all polygons in this object. @@ -1901,9 +1912,9 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: if self.rest_cb.isChecked(): - app_obj.new_object("geometry", name, gen_paintarea_rest_machining) + app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) else: - app_obj.new_object("geometry", name, gen_paintarea) + app_obj.new_object("geometry", name, gen_paintarea, plot=plot) except FlatCAMApp.GracefulException: proc.done() return @@ -1920,8 +1931,11 @@ class ToolPaint(FlatCAMTool, Gerber): # Promise object with the new name self.app.collection.promise(name) - # Background - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + if run_threaded: + # Background + self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + else: + job_thread(app_obj=self.app) def paint_poly_area(self, obj, sel_obj, tooldia=None, @@ -1932,7 +1946,9 @@ class ToolPaint(FlatCAMTool, Gerber): outname=None, connect=None, contour=None, - tools_storage=None): + tools_storage=None, + plot=True, + run_threaded=True): """ Paints all polygons in this object that are within the sel_obj object @@ -2366,9 +2382,9 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: if self.rest_cb.isChecked(): - app_obj.new_object("geometry", name, gen_paintarea_rest_machining) + app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) else: - app_obj.new_object("geometry", name, gen_paintarea) + app_obj.new_object("geometry", name, gen_paintarea, plot=plot) except FlatCAMApp.GracefulException: proc.done() return @@ -2385,8 +2401,11 @@ class ToolPaint(FlatCAMTool, Gerber): # Promise object with the new name self.app.collection.promise(name) - # Background - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + if run_threaded: + # Background + self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + else: + job_thread(app_obj=self.app) def paint_poly_ref(self, obj, sel_obj, tooldia=None, @@ -2397,7 +2416,9 @@ class ToolPaint(FlatCAMTool, Gerber): outname=None, connect=None, contour=None, - tools_storage=None): + tools_storage=None, + plot=True, + run_threaded=True): """ Paints all polygons in this object that are within the sel_obj object @@ -2441,7 +2462,9 @@ class ToolPaint(FlatCAMTool, Gerber): outname=outname, connect=connect, contour=contour, - tools_storage=tools_storage) + tools_storage=tools_storage, + plot=plot, + run_threaded=run_threaded) @staticmethod def paint_bounds(geometry): diff --git a/share/bug16.png b/share/bug16.png index ba62ffd9..320aac33 100644 Binary files a/share/bug16.png and b/share/bug16.png differ diff --git a/share/bug32.png b/share/bug32.png new file mode 100644 index 00000000..97ec1b73 Binary files /dev/null and b/share/bug32.png differ diff --git a/share/pdf_link16.png b/share/pdf_link16.png new file mode 100644 index 00000000..22c858cb Binary files /dev/null and b/share/pdf_link16.png differ diff --git a/tclCommands/TclCommand.py b/tclCommands/TclCommand.py index b50b380c..e279fee2 100644 --- a/tclCommands/TclCommand.py +++ b/tclCommands/TclCommand.py @@ -78,8 +78,7 @@ class TclCommand(object): :return: current command """ - - command_string = [] + command_string = list() command_string.append(self.aliases[0]) if self.original_args is not None: @@ -117,7 +116,7 @@ class TclCommand(object): if help_key in self.arg_names: arg_type = self.arg_names[help_key] type_name = str(arg_type.__name__) - #in_command_name = help_key + "<" + type_name + ">" + # in_command_name = help_key + "<" + type_name + ">" in_command_name = help_key elif help_key in self.option_types: @@ -163,35 +162,36 @@ class TclCommand(object): @staticmethod def parse_arguments(args): - """ - Pre-processes arguments to detect '-keyword value' pairs into dictionary - and standalone parameters into list. + """ + Pre-processes arguments to detect '-keyword value' pairs into dictionary + and standalone parameters into list. - This is copy from FlatCAMApp.setup_shell().h() just for accessibility, - original should be removed after all commands will be converted + This is copy from FlatCAMApp.setup_shell().h() just for accessibility, + original should be removed after all commands will be converted - :param args: arguments from tcl to parse - :return: arguments, options - """ + :param args: arguments from tcl to parse + :return: arguments, options + """ - options = {} - arguments = [] - n = len(args) - name = None - for i in range(n): - match = re.search(r'^-([a-zA-Z].*)', args[i]) - if match: - assert name is None - name = match.group(1) - continue + options = {} + arguments = [] + n = len(args) - if name is None: - arguments.append(args[i]) - else: - options[name] = args[i] - name = None + name = None + for i in range(n): + match = re.search(r'^-([a-zA-Z].*)', args[i]) + if match: + assert name is None + name = match.group(1) + continue - return arguments, options + if name is None: + arguments.append(args[i]) + else: + options[name] = args[i] + name = None + + return arguments, options def check_args(self, args): """ @@ -274,7 +274,6 @@ class TclCommand(object): """ # self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]}) - try: self.log.debug("TCL command '%s' executed." % str(self.__class__)) self.original_args = args @@ -416,7 +415,6 @@ class TclCommandSignaled(TclCommand): # when operation will be really long is good to set it higher then defqault 30s self.app.worker_task.emit({'fcn': self.execute_call, 'params': [args, unnamed_args]}) - return self.output except Exception as unknown: diff --git a/tclCommands/TclCommandAlignDrill.py b/tclCommands/TclCommandAlignDrill.py index 6fbaf7c0..7ce59b2a 100644 --- a/tclCommands/TclCommandAlignDrill.py +++ b/tclCommands/TclCommandAlignDrill.py @@ -29,6 +29,7 @@ class TclCommandAlignDrill(TclCommandSignaled): ('axisoffset', float), ('dia', float), ('dist', float), + ('outname', str), ]) # array of mandatory options for current Tcl command: required = {'name','outname'} @@ -47,9 +48,11 @@ class TclCommandAlignDrill(TclCommandSignaled): ('minoffset', 'min and max distance between align hole and pcb.'), ('axisoffset', 'Offset on second axis before aligment holes'), ('axis', 'Mirror axis parallel to the X or Y axis.'), - ('dist', 'Distance of the mirror axis to the X or Y axis.') + ('dist', 'Distance of the mirror axis to the X or Y axis.'), + ('outname', 'Name of the resulting Excellon object.'), ]), - 'examples': [] + 'examples': ['aligndrill my_object -axis X -box my_object -dia 3.125 -grid 1 ' + '-gridoffset 0 -minoffset 2 -axisoffset 2'] } def execute(self, args, unnamed_args): @@ -64,6 +67,11 @@ class TclCommandAlignDrill(TclCommandSignaled): name = args['name'] + if 'outname' in args: + outname = args['outname'] + else: + outname = name + "_aligndrill" + # Get source object. try: obj = self.app.collection.get_by_name(str(name)) @@ -176,9 +184,7 @@ class TclCommandAlignDrill(TclCommandSignaled): px = 0.5 * (xmin + xmax) py = 0.5 * (ymin + ymax) - obj.app.new_object("excellon", - name + "_aligndrill", - alligndrill_init_me) + obj.app.new_object("excellon", outname, alligndrill_init_me, plot=False) except Exception as e: return "Operation failed: %s" % str(e) @@ -194,8 +200,8 @@ class TclCommandAlignDrill(TclCommandSignaled): try: px = dist py = dist - obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me) + obj.app.new_object("excellon", outname, alligndrill_init_me, plot=False) except Exception as e: return "Operation failed: %s" % str(e) - return 'Ok' + return 'Ok. Align Drills Excellon object created' diff --git a/tclCommands/TclCommandAlignDrillGrid.py b/tclCommands/TclCommandAlignDrillGrid.py index 39bc587d..bdfa014d 100644 --- a/tclCommands/TclCommandAlignDrillGrid.py +++ b/tclCommands/TclCommandAlignDrillGrid.py @@ -17,7 +17,7 @@ class TclCommandAlignDrillGrid(TclCommandSignaled): # Dictionary of types from Tcl command, needs to be ordered. # For positional arguments arg_names = collections.OrderedDict([ - ('outname', str) + ]) # Dictionary of types from Tcl command, needs to be ordered. @@ -29,11 +29,12 @@ class TclCommandAlignDrillGrid(TclCommandSignaled): ('gridy', float), ('gridoffsety', float), ('columns', int), - ('rows', int) + ('rows', int), + ('outname', str) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} - required = ['outname', 'gridx', 'gridy', 'columns', 'rows'] + required = ['gridx', 'gridy', 'columns', 'rows'] # structured help for current command, args needs to be ordered help = { @@ -48,7 +49,7 @@ class TclCommandAlignDrillGrid(TclCommandSignaled): ('colums', 'Number of grid holes on X axis.'), ('rows', 'Number of grid holes on Y axis.'), ]), - 'examples': [] + 'examples': ['aligndrillgrid -rows 2 -columns 2 -gridoffsetx 10 -gridoffsety 10 -gridx 2.54 -gridy 5.08'] } def execute(self, args, unnamed_args): @@ -61,6 +62,11 @@ class TclCommandAlignDrillGrid(TclCommandSignaled): :return: None or exception """ + if 'outname' in args: + outname = args['outname'] + else: + outname = "new_aligndrill_grid" + if 'gridoffsetx' not in args: gridoffsetx = 0 else: @@ -102,4 +108,4 @@ class TclCommandAlignDrillGrid(TclCommandSignaled): init_obj.create_geometry() # Create the new object - self.app.new_object("excellon", args['outname'], aligndrillgrid_init_me) + self.app.new_object("excellon", outname, aligndrillgrid_init_me, plot=False) diff --git a/tclCommands/TclCommandBbox.py b/tclCommands/TclCommandBbox.py index dbbabe8c..d1ecd954 100644 --- a/tclCommands/TclCommandBbox.py +++ b/tclCommands/TclCommandBbox.py @@ -90,6 +90,6 @@ class TclCommandBbox(TclCommand): bounding_box = bounding_box.envelope geo_obj.solid_geometry = bounding_box - self.app.new_object("geometry", args['outname'], geo_init) + self.app.new_object("geometry", args['outname'], geo_init, plot=False) except Exception as e: return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandCncjob.py b/tclCommands/TclCommandCncjob.py index 3415dd4e..857fa5c7 100644 --- a/tclCommands/TclCommandCncjob.py +++ b/tclCommands/TclCommandCncjob.py @@ -24,7 +24,7 @@ class TclCommandCncjob(TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('tooldia', float), + ('dia', float), ('z_cut', float), ('z_move', float), ('feedrate', float), @@ -42,6 +42,7 @@ class TclCommandCncjob(TclCommandSignaled): ('dwell', bool), ('dwelltime', float), ('pp', str), + ('muted', int), ('outname', str) ]) @@ -53,7 +54,7 @@ class TclCommandCncjob(TclCommandSignaled): 'main': "Generates a CNC Job from a Geometry Object.", 'args': collections.OrderedDict([ ('name', 'Name of the source object.'), - ('tooldia', 'Tool diameter to show on screen.'), + ('dia', 'Tool diameter to show on screen.'), ('z_cut', 'Z-axis cutting position.'), ('z_move', 'Z-axis moving position.'), ('feedrate', 'Moving speed on X-Y plane when cutting.'), @@ -71,7 +72,8 @@ class TclCommandCncjob(TclCommandSignaled): ('dwell', 'True or False; use (or not) the dwell'), ('dwelltime', 'Time to pause to allow the spindle to reach the full speed'), ('outname', 'Name of the resulting Geometry object.'), - ('pp', 'Name of the Geometry postprocessor. No quotes, case sensitive') + ('pp', 'Name of the Geometry postprocessor. No quotes, case sensitive'), + ('muted', 'It will not put errors in the Shell.') ]), 'examples': ['cncjob geo_name -tooldia 0.5 -z_cut -1.7 -z_move 2 -feedrate 120 -ppname_g default'] } @@ -91,15 +93,26 @@ class TclCommandCncjob(TclCommandSignaled): if 'outname' not in args: args['outname'] = str(name) + "_cnc" + if 'muted' in args: + muted = args['muted'] + else: + muted = 0 + obj = self.app.collection.get_by_name(str(name), isCaseSensitive=False) if obj is None: - self.raise_tcl_error("Object not found: %s" % str(name)) + if not muted: + self.raise_tcl_error("Object not found: %s" % str(name)) + else: + return if not isinstance(obj, FlatCAMGeometry): - self.raise_tcl_error('Expected FlatCAMGeometry, got %s %s.' % (str(name), type(obj))) + if not muted: + self.raise_tcl_error('Expected FlatCAMGeometry, got %s %s.' % (str(name), type(obj))) + else: + return - args["tooldia"] = args["tooldia"] if "tooldia" in args else obj.options["cnctooldia"] + args["dia"] = args["dia"] if "dia" in args else obj.options["cnctooldia"] args["z_cut"] = args["z_cut"] if "z_cut" in args else obj.options["cutz"] args["z_move"] = args["z_move"] if "z_move" in args else obj.options["travelz"] @@ -130,20 +143,24 @@ class TclCommandCncjob(TclCommandSignaled): del args['name'] for arg in args: - if arg == "toolchange_xy" or arg == "spindlespeed": + if arg == "toolchange_xy" or arg == "spindlespeed" or arg == "startz": continue else: if args[arg] is None: - self.raise_tcl_error('One of the command parameters that have to be not None, is None.\n' - 'The parameter that is None is in the default values found in the list \n' - 'generated by the TclCommand "list_sys geom". or in the arguments.') + print(arg, args[arg]) + if not muted: + self.raise_tcl_error('One of the command parameters that have to be not None, is None.\n' + 'The parameter that is None is in the default values found in the list \n' + 'generated by the TclCommand "list_sys geom". or in the arguments.') + else: + return # HACK !!! Should be solved elsewhere!!! # default option for multidepth is False obj.options['multidepth'] = False if not obj.multigeo: - obj.generatecncjob(use_thread=False, **args) + obj.generatecncjob(use_thread=False, plot=False, **args) else: # Update the local_tools_dict values with the args value local_tools_dict = deepcopy(obj.tools) @@ -171,5 +188,6 @@ class TclCommandCncjob(TclCommandSignaled): outname=args['outname'], tools_dict=local_tools_dict, tools_in_use=[], - use_thread=False) + use_thread=False, + plot=False) # self.raise_tcl_error('The object is a multi-geo geometry which is not supported in cncjob Tcl Command') diff --git a/tclCommands/TclCommandCopperClear.py b/tclCommands/TclCommandCopperClear.py index 38b0aa90..cc22a4f2 100644 --- a/tclCommands/TclCommandCopperClear.py +++ b/tclCommands/TclCommandCopperClear.py @@ -226,6 +226,7 @@ class TclCommandCopperClear(TclCommand): contour=contour, rest=rest, tools_storage=ncc_tools, + plot=False, run_threaded=False) return @@ -259,6 +260,7 @@ class TclCommandCopperClear(TclCommand): contour=contour, rest=rest, tools_storage=ncc_tools, + plot=False, run_threaded=False) return else: diff --git a/tclCommands/TclCommandCutout.py b/tclCommands/TclCommandCutout.py index e4826acc..782d174b 100644 --- a/tclCommands/TclCommandCutout.py +++ b/tclCommands/TclCommandCutout.py @@ -123,7 +123,7 @@ class TclCommandCutout(TclCommand): geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts]) try: - self.app.new_object("geometry", name + "_cutout", geo_init_me) + self.app.new_object("geometry", name + "_cutout", geo_init_me, plot=False) self.app.inform.emit("[success] Rectangular-form Cutout operation finished.") except Exception as e: return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 4a7bb4cd..9b5848ed 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -17,7 +17,6 @@ class TclCommandDrillcncjob(TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('tools', str), ('drilled_dias', str), ('drillz', float), ('travelz', float), @@ -31,7 +30,8 @@ class TclCommandDrillcncjob(TclCommandSignaled): ('pp', str), ('outname', str), ('opt_type', str), - ('diatol', float) + ('diatol', float), + ('muted', int) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} @@ -62,7 +62,8 @@ class TclCommandDrillcncjob(TclCommandSignaled): 'the same as the ones in the tools from the Excellon object. E.g: if in drill_dias we have a ' 'diameter with value 1.0, in the Excellon we have a tool with dia = 1.05 and we set a tolerance ' 'diatol = 5.0 then the drills with the dia = (0.95 ... 1.05) ' - 'in Excellon will be processed. Float number.') + 'in Excellon will be processed. Float number.'), + ('muted', 'It will not put errors in the Shell or status bar.') ]), 'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate 222 -feedrate_rapid 456 -spindlespeed 777' ' -toolchange True -toolchangez 33 -endz 22 -pp default\n' @@ -84,12 +85,20 @@ class TclCommandDrillcncjob(TclCommandSignaled): if 'outname' not in args: args['outname'] = name + "_cnc" + if 'muted' in args: + muted = args['muted'] + else: + muted = 0 + obj = self.app.collection.get_by_name(name) if obj is None: self.raise_tcl_error("Object not found: %s" % name) if not isinstance(obj, FlatCAMExcellon): - self.raise_tcl_error('Expected FlatCAMExcellon, got %s %s.' % (name, type(obj))) + if not muted: + self.raise_tcl_error('Expected FlatCAMExcellon, got %s %s.' % (name, type(obj))) + else: + return xmin = obj.options['xmin'] ymin = obj.options['ymin'] @@ -127,8 +136,11 @@ class TclCommandDrillcncjob(TclCommandSignaled): nr_diameters -= 1 if nr_diameters > 0: - self.raise_tcl_error("One or more tool diameters of the drills to be drilled passed to the " - "TclCommand are not actual tool diameters in the Excellon object.") + if not muted: + self.raise_tcl_error("One or more tool diameters of the drills to be drilled passed to the " + "TclCommand are not actual tool diameters in the Excellon object.") + else: + return # make a string of diameters separated by comma; this is what generate_from_excellon_by_tool() is # expecting as tools parameter @@ -181,4 +193,4 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.gcode_parse() job_obj.create_geometry() - self.app.new_object("cncjob", args['outname'], job_init) + self.app.new_object("cncjob", args['outname'], job_init, plot=False) diff --git a/tclCommands/TclCommandExteriors.py b/tclCommands/TclCommandExteriors.py index 6c93bb59..c4737c3a 100644 --- a/tclCommands/TclCommandExteriors.py +++ b/tclCommands/TclCommandExteriors.py @@ -61,4 +61,4 @@ class TclCommandExteriors(TclCommandSignaled): geo_obj.solid_geometry = obj_exteriors obj_exteriors = obj.get_exteriors() - self.app.new_object('geometry', outname, geo_init) + self.app.new_object('geometry', outname, geo_init, plot=False) diff --git a/tclCommands/TclCommandGeoCutout.py b/tclCommands/TclCommandGeoCutout.py index 4a32df88..6e2bf86f 100644 --- a/tclCommands/TclCommandGeoCutout.py +++ b/tclCommands/TclCommandGeoCutout.py @@ -279,7 +279,7 @@ class TclCommandGeoCutout(TclCommandSignaled): app_obj.inform.emit("[success] Any-form Cutout operation finished.") outname = cutout_obj.options["name"] + "_cutout" - self.app.new_object('geometry', outname, geo_init) + self.app.new_object('geometry', outname, geo_init, plot=False) # cutout_obj.plot() # self.app.inform.emit("[success] Any-form Cutout operation finished.") @@ -338,7 +338,7 @@ class TclCommandGeoCutout(TclCommandSignaled): app_obj.inform.emit("[success] Any-form Cutout operation finished.") outname = cutout_obj.options["name"] + "_cutout" - self.app.new_object('geometry', outname, geo_init) + self.app.new_object('geometry', outname, geo_init, plot=False) cutout_obj = self.app.collection.get_by_name(outname) else: diff --git a/tclCommands/TclCommandImportSvg.py b/tclCommands/TclCommandImportSvg.py index 01317b6b..d29eb99a 100644 --- a/tclCommands/TclCommandImportSvg.py +++ b/tclCommands/TclCommandImportSvg.py @@ -71,7 +71,7 @@ class TclCommandImportSvg(TclCommandSignaled): with self.app.proc_container.new("Import SVG"): # Object creation - self.app.new_object(obj_type, outname, obj_init) + self.app.new_object(obj_type, outname, obj_init, plot=False) # Register recent file self.app.file_opened.emit("svg", filename) diff --git a/tclCommands/TclCommandIsolate.py b/tclCommands/TclCommandIsolate.py index 4b57d409..eddf1527 100644 --- a/tclCommands/TclCommandIsolate.py +++ b/tclCommands/TclCommandIsolate.py @@ -85,4 +85,4 @@ class TclCommandIsolate(TclCommandSignaled): self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj))) del args['name'] - obj.isolate(**args) + obj.isolate(plot=False, **args) diff --git a/tclCommands/TclCommandJoinExcellon.py b/tclCommands/TclCommandJoinExcellon.py index ef2654ca..60a371fc 100644 --- a/tclCommands/TclCommandJoinExcellon.py +++ b/tclCommands/TclCommandJoinExcellon.py @@ -61,4 +61,4 @@ class TclCommandJoinExcellon(TclCommand): FlatCAMExcellon.merge(objs, obj_) if objs is not None: - self.app.new_object("excellon", outname, initialize) + self.app.new_object("excellon", outname, initialize, plot=False) diff --git a/tclCommands/TclCommandJoinGeometry.py b/tclCommands/TclCommandJoinGeometry.py index 90beac4f..005a2e09 100644 --- a/tclCommands/TclCommandJoinGeometry.py +++ b/tclCommands/TclCommandJoinGeometry.py @@ -61,4 +61,4 @@ class TclCommandJoinGeometry(TclCommand): FlatCAMGeometry.merge(objs, obj_) if objs is not None: - self.app.new_object("geometry", outname, initialize) + self.app.new_object("geometry", outname, initialize, plot=False) diff --git a/tclCommands/TclCommandMillDrills.py b/tclCommands/TclCommandMillDrills.py index 2919ab89..5dc7c9d3 100644 --- a/tclCommands/TclCommandMillDrills.py +++ b/tclCommands/TclCommandMillDrills.py @@ -128,7 +128,7 @@ class TclCommandMillDrills(TclCommandSignaled): del args['name'] # This runs in the background... Is blocking handled? - success, msg = obj.generate_milling_drills(**args) + success, msg = obj.generate_milling_drills(plot=False, **args) except Exception as e: success = None msg = None diff --git a/tclCommands/TclCommandMillSlots.py b/tclCommands/TclCommandMillSlots.py index 22159e4b..bb6882d2 100644 --- a/tclCommands/TclCommandMillSlots.py +++ b/tclCommands/TclCommandMillSlots.py @@ -127,7 +127,7 @@ class TclCommandMillSlots(TclCommandSignaled): del args['name'] # This runs in the background... Is blocking handled? - success, msg = obj.generate_milling_slots(**args) + success, msg = obj.generate_milling_slots(plot=False, **args) except Exception as e: success = None diff --git a/tclCommands/TclCommandMirror.py b/tclCommands/TclCommandMirror.py index db7ec3c8..821e91b3 100644 --- a/tclCommands/TclCommandMirror.py +++ b/tclCommands/TclCommandMirror.py @@ -103,6 +103,5 @@ class TclCommandMirror(TclCommandSignaled): try: obj.mirror(axis, [dist, dist]) - obj.plot() except Exception as e: return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandNew.py b/tclCommands/TclCommandNew.py index bf386d0c..3ad7eb76 100644 --- a/tclCommands/TclCommandNew.py +++ b/tclCommands/TclCommandNew.py @@ -36,4 +36,4 @@ class TclCommandNew(TclCommand): :return: None or exception """ - self.app.on_file_new() + self.app.on_file_new(cli=True) diff --git a/tclCommands/TclCommandNewExcellon.py b/tclCommands/TclCommandNewExcellon.py new file mode 100644 index 00000000..22f81347 --- /dev/null +++ b/tclCommands/TclCommandNewExcellon.py @@ -0,0 +1,51 @@ +from ObjectCollection import * +from tclCommands.TclCommand import TclCommandSignaled + + +class TclCommandNewExcellon(TclCommandSignaled): + """ + Tcl shell command to subtract polygon from the given Geometry object. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['new_excellon'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('name', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered. + # For options like -optionname value + option_types = collections.OrderedDict([ + + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = [] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Creates a new empty Excellon object.", + 'args': collections.OrderedDict([ + ('name', 'New object name.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + if 'name' in args: + name = args['name'] + else: + name = 'new_exc' + self.app.new_object('excellon', name, lambda x, y: None, plot=False) diff --git a/tclCommands/TclCommandNewGeometry.py b/tclCommands/TclCommandNewGeometry.py index 349f8842..746a8d85 100644 --- a/tclCommands/TclCommandNewGeometry.py +++ b/tclCommands/TclCommandNewGeometry.py @@ -23,7 +23,7 @@ class TclCommandNewGeometry(TclCommandSignaled): ]) # array of mandatory options for current Tcl command: required = {'name','outname'} - required = ['name'] + required = [] # structured help for current command, args needs to be ordered help = { @@ -43,7 +43,9 @@ class TclCommandNewGeometry(TclCommandSignaled): without -somename and we do not have them in known arg_names :return: None or exception """ + if 'name' in args: + name = args['name'] + else: + name = 'new_geo' - name = args['name'] - - self.app.new_object('geometry', str(name), lambda x, y: None) + self.app.new_object('geometry', str(name), lambda x, y: None, plot=False) diff --git a/tclCommands/TclCommandNewGerber.py b/tclCommands/TclCommandNewGerber.py new file mode 100644 index 00000000..c461d83d --- /dev/null +++ b/tclCommands/TclCommandNewGerber.py @@ -0,0 +1,68 @@ +from ObjectCollection import * +from tclCommands.TclCommand import TclCommandSignaled + + +class TclCommandNewGerber(TclCommandSignaled): + """ + Tcl shell command to subtract polygon from the given Geometry object. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['new_gerber'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('name', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered. + # For options like -optionname value + option_types = collections.OrderedDict([ + + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = [] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Creates a new empty Gerber object.", + 'args': collections.OrderedDict([ + ('name', 'New object name.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + if 'name' in args: + name = args['name'] + else: + name = 'new_grb' + + def initialize(grb_obj, self): + grb_obj.multitool = False + grb_obj.source_file = [] + grb_obj.multigeo = False + grb_obj.follow = False + grb_obj.apertures = {} + grb_obj.solid_geometry = [] + + try: + grb_obj.options['xmin'] = 0 + grb_obj.options['ymin'] = 0 + grb_obj.options['xmax'] = 0 + grb_obj.options['ymax'] = 0 + except KeyError: + pass + + self.app.new_object('gerber', name, initialize, plot=False) diff --git a/tclCommands/TclCommandNregions.py b/tclCommands/TclCommandNregions.py index 972be3d7..34cd43b9 100644 --- a/tclCommands/TclCommandNregions.py +++ b/tclCommands/TclCommandNregions.py @@ -89,7 +89,7 @@ class TclCommandNregions(TclCommand): non_copper = bounding_box.difference(geo) geo_obj.solid_geometry = non_copper - self.app.new_object("geometry", args['outname'], geo_init) + self.app.new_object("geometry", args['outname'], geo_init, plot=False) except Exception as e: return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandOpenExcellon.py b/tclCommands/TclCommandOpenExcellon.py index 69f8ce70..253221cd 100644 --- a/tclCommands/TclCommandOpenExcellon.py +++ b/tclCommands/TclCommandOpenExcellon.py @@ -46,5 +46,7 @@ class TclCommandOpenExcellon(TclCommandSignaled): """ filename = args.pop('filename') + filename = filename.replace(' ', '') + args['plot'] = False self.app.open_excellon(filename, **args) diff --git a/tclCommands/TclCommandOpenGCode.py b/tclCommands/TclCommandOpenGCode.py index 99314365..08ada089 100644 --- a/tclCommands/TclCommandOpenGCode.py +++ b/tclCommands/TclCommandOpenGCode.py @@ -45,5 +45,8 @@ class TclCommandOpenGCode(TclCommandSignaled): without -somename and we do not have them in known arg_names :return: None or exception """ + args['plot'] = False + filename = args["filename"] + filename = filename.replace(' ', '') - self.app.open_gcode(args['filename'], **args) + self.app.open_gcode(filename, **args) diff --git a/tclCommands/TclCommandOpenGerber.py b/tclCommands/TclCommandOpenGerber.py index 16e82b2a..18eae168 100644 --- a/tclCommands/TclCommandOpenGerber.py +++ b/tclCommands/TclCommandOpenGerber.py @@ -30,7 +30,7 @@ class TclCommandOpenGerber(TclCommandSignaled): ('filename', 'Path to file to open.'), ('outname', 'Name of the resulting Gerber object.') ]), - 'examples': [] + 'examples': ["open_gerber gerber_object_path -outname bla"] } def execute(self, args, unnamed_args): @@ -50,25 +50,19 @@ class TclCommandOpenGerber(TclCommandSignaled): self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (outname, type(gerber_obj))) # Opening the file happens here - self.app.progress.emit(30) try: gerber_obj.parse_file(filename) - except IOError: app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: %s " % filename) - app_obj.progress.emit(0) self.raise_tcl_error('Failed to open file: %s' % filename) except ParseError as e: app_obj.inform.emit("[ERROR_NOTCL] Failed to parse file: %s, %s " % (filename, str(e))) - app_obj.progress.emit(0) self.log.error(str(e)) return - # Further parsing - app_obj.progress.emit(70) - filename = args['filename'] + filename = filename.replace(' ', '') if 'outname' in args: outname = args['outname'] @@ -76,17 +70,16 @@ class TclCommandOpenGerber(TclCommandSignaled): outname = filename.split('/')[-1].split('\\')[-1] if 'follow' in args: - self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' parameter for the Tcl Command isolate()") + self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' " + "parameter for the Tcl Command isolate()") with self.app.proc_container.new("Opening Gerber"): # Object creation - self.app.new_object("gerber", outname, obj_init) + self.app.new_object("gerber", outname, obj_init, plot=False) # Register recent file self.app.file_opened.emit("gerber", filename) - self.app.progress.emit(100) - # GUI feedback self.app.inform.emit("[success] Opened: " + filename) diff --git a/tclCommands/TclCommandOpenProject.py b/tclCommands/TclCommandOpenProject.py index 97d39ad6..eab891e9 100644 --- a/tclCommands/TclCommandOpenProject.py +++ b/tclCommands/TclCommandOpenProject.py @@ -43,5 +43,7 @@ class TclCommandOpenProject(TclCommandSignaled): without -somename and we do not have them in known arg_names :return: None or exception """ + filename = args['filename'] + filename = filename.replace(' ', '') - self.app.open_project(args['filename']) + self.app.open_project(filename, cli=True, plot=False) diff --git a/tclCommands/TclCommandPaint.py b/tclCommands/TclCommandPaint.py index 300ccbbd..4263b84c 100644 --- a/tclCommands/TclCommandPaint.py +++ b/tclCommands/TclCommandPaint.py @@ -201,7 +201,9 @@ class TclCommandPaint(TclCommand): outname=outname, connect=connect, contour=contour, - tools_storage=paint_tools) + tools_storage=paint_tools, + plot=False, + run_threaded=False) return # Paint single polygon in the painted object @@ -222,7 +224,9 @@ class TclCommandPaint(TclCommand): outname=outname, connect=connect, contour=contour, - tools_storage=paint_tools) + tools_storage=paint_tools, + plot=False, + run_threaded=False) return # Paint all polygons found within the box object from the the painted object @@ -250,7 +254,9 @@ class TclCommandPaint(TclCommand): outname=outname, connect=connect, contour=contour, - tools_storage=paint_tools) + tools_storage=paint_tools, + plot=False, + run_threaded=False) return else: diff --git a/tclCommands/TclCommandPanelize.py b/tclCommands/TclCommandPanelize.py index 8c2f8d67..3497ad0a 100644 --- a/tclCommands/TclCommandPanelize.py +++ b/tclCommands/TclCommandPanelize.py @@ -90,7 +90,7 @@ class TclCommandPanelize(TclCommand): if 'threaded' in args: threaded = args['threaded'] else: - threaded = 1 + threaded = 0 if 'spacing_columns' in args: spacing_columns = args['spacing_columns'] @@ -265,10 +265,10 @@ class TclCommandPanelize(TclCommand): if isinstance(obj, FlatCAMExcellon): self.app.progress.emit(50) - self.app.new_object("excellon", outname, job_init_excellon, plot=True, autoselected=True) + self.app.new_object("excellon", outname, job_init_excellon, plot=False, autoselected=True) else: self.app.progress.emit(50) - self.app.new_object("geometry", outname, job_init_geometry, plot=True, autoselected=True) + self.app.new_object("geometry", outname, job_init_geometry, plot=False, autoselected=True) if threaded == 1: proc = self.app.proc_container.new("Generating panel ... Please wait.") diff --git a/tclCommands/TclCommandPlot.py b/tclCommands/TclCommandPlotAll.py similarity index 94% rename from tclCommands/TclCommandPlot.py rename to tclCommands/TclCommandPlotAll.py index d9d045b0..349eeb2f 100644 --- a/tclCommands/TclCommandPlot.py +++ b/tclCommands/TclCommandPlotAll.py @@ -2,7 +2,7 @@ from ObjectCollection import * from tclCommands.TclCommand import TclCommand -class TclCommandPlot(TclCommand): +class TclCommandPlotAll(TclCommand): """ Tcl shell command to update the plot on the user interface. @@ -11,7 +11,7 @@ class TclCommandPlot(TclCommand): """ # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) - aliases = ['plot'] + aliases = ['plot_all'] # Dictionary of types from Tcl command, needs to be ordered arg_names = collections.OrderedDict([ diff --git a/tclCommands/TclCommandPlotObjects.py b/tclCommands/TclCommandPlotObjects.py new file mode 100644 index 00000000..f7a418d7 --- /dev/null +++ b/tclCommands/TclCommandPlotObjects.py @@ -0,0 +1,51 @@ +from ObjectCollection import * +from tclCommands.TclCommand import TclCommand + + +class TclCommandPlotObjects(TclCommand): + """ + Tcl shell command to update the plot on the user interface. + + example: + plot + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['plot_objects'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('names', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value + option_types = collections.OrderedDict([ + + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = [] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Plot a list of objects.", + 'args': collections.OrderedDict([ + ('names', "UA list of object names to be plotted.") + ]), + 'examples': ["plot_objects"] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + names = [x.strip() for x in args['names'].split(",")] + objs = [] + for name in names: + objs.append(self.app.collection.get_by_name(name)) + + for obj in objs: + obj.plot() diff --git a/tclCommands/TclCommandWriteGCode.py b/tclCommands/TclCommandWriteGCode.py index d1b1c9d4..b27fb44e 100644 --- a/tclCommands/TclCommandWriteGCode.py +++ b/tclCommands/TclCommandWriteGCode.py @@ -22,7 +22,8 @@ class TclCommandWriteGCode(TclCommandSignaled): # For options like -optionname value option_types = collections.OrderedDict([ ('preamble', str), - ('postamble', str) + ('postamble', str), + ('muted', int) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} @@ -35,7 +36,9 @@ class TclCommandWriteGCode(TclCommandSignaled): ('name', 'Source CNC Job object.'), ('filename', 'Output filename.'), ('preamble', 'Text to append at the beginning.'), - ('postamble', 'Text to append at the end.') + ('postamble', 'Text to append at the end.'), + ('muted', 'It will not put errors in the Shell or status bar.') + ]), 'examples': ["write_gcode name c:\\\\gcode_repo"] } @@ -62,6 +65,11 @@ class TclCommandWriteGCode(TclCommandSignaled): preamble = args['preamble'] if 'preamble' in args else '' postamble = args['postamble'] if 'postamble' in args else '' + if 'muted' in args: + muted = args['muted'] + else: + muted = 0 + # TODO: This is not needed any more? All targets should be present. # If there are promised objects, wait until all promises have been fulfilled. # if self.collection.has_promises(): @@ -82,9 +90,15 @@ class TclCommandWriteGCode(TclCommandSignaled): try: obj = self.app.collection.get_by_name(str(obj_name)) except: - return "Could not retrieve object: %s" % obj_name + if not muted: + return "Could not retrieve object: %s" % obj_name + else: + return try: obj.export_gcode(str(filename), str(preamble), str(postamble)) except Exception as e: - return "Operation failed: %s" % str(e) + if not muted: + return "Operation failed: %s" % str(e) + else: + return diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py index 26f5c2e3..98603aa5 100644 --- a/tclCommands/__init__.py +++ b/tclCommands/__init__.py @@ -35,7 +35,9 @@ import tclCommands.TclCommandMillSlots import tclCommands.TclCommandMirror import tclCommands.TclCommandNew import tclCommands.TclCommandNregions +import tclCommands.TclCommandNewExcellon import tclCommands.TclCommandNewGeometry +import tclCommands.TclCommandNewGerber import tclCommands.TclCommandOffset import tclCommands.TclCommandOpenExcellon import tclCommands.TclCommandOpenGCode @@ -44,7 +46,8 @@ import tclCommands.TclCommandOpenProject import tclCommands.TclCommandOptions import tclCommands.TclCommandPaint import tclCommands.TclCommandPanelize -import tclCommands.TclCommandPlot +import tclCommands.TclCommandPlotAll +import tclCommands.TclCommandPlotObjects import tclCommands.TclCommandSaveProject import tclCommands.TclCommandSaveSys import tclCommands.TclCommandScale