Merged in marius_stanciu/flatcam_beta/Beta (pull request #184)

Beta
This commit is contained in:
Marius Stanciu 2019-08-23 23:56:25 +00:00
commit 43dcdef14a
41 changed files with 27504 additions and 23350 deletions

View File

@ -98,8 +98,8 @@ class App(QtCore.QObject):
# ####################################
# Version and VERSION DATE ###########
# ####################################
version = 8.95
version_date = "2019/08/17"
version = 8.96
version_date = "2019/08/23"
beta = True
# current date now
@ -183,6 +183,9 @@ class App(QtCore.QObject):
# in the worker task.
thread_exception = QtCore.pyqtSignal(object)
# used to signal that there are arguments for the app
args_at_startup = QtCore.pyqtSignal()
def __init__(self, user_defaults=True, post_gui=None):
"""
Starts the application.
@ -320,7 +323,6 @@ class App(QtCore.QObject):
QtCore.QObject.__init__(self)
self.ui = FlatCAMGUI(self.version, self.beta, self)
self.set_ui_title(name=_("New Project - Not saved"))
self.ui.geom_update[int, int, int, int, int].connect(self.save_geometry)
self.ui.final_save.connect(self.final_save)
@ -345,6 +347,7 @@ class App(QtCore.QObject):
# General App
"units": self.ui.general_defaults_form.general_app_group.units_radio,
"global_app_level": self.ui.general_defaults_form.general_app_group.app_level_radio,
"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,
@ -420,6 +423,21 @@ class App(QtCore.QObject):
# Gerber Editor
"gerber_editor_sel_limit": self.ui.gerber_defaults_form.gerber_editor_group.sel_limit_entry,
"gerber_editor_newcode": self.ui.gerber_defaults_form.gerber_editor_group.addcode_entry,
"gerber_editor_newsize": self.ui.gerber_defaults_form.gerber_editor_group.addsize_entry,
"gerber_editor_newtype": self.ui.gerber_defaults_form.gerber_editor_group.addtype_combo,
"gerber_editor_newdim": self.ui.gerber_defaults_form.gerber_editor_group.adddim_entry,
"gerber_editor_array_size": self.ui.gerber_defaults_form.gerber_editor_group.grb_array_size_entry,
"gerber_editor_lin_axis": self.ui.gerber_defaults_form.gerber_editor_group.grb_axis_radio,
"gerber_editor_lin_pitch": self.ui.gerber_defaults_form.gerber_editor_group.grb_pitch_entry,
"gerber_editor_lin_angle": self.ui.gerber_defaults_form.gerber_editor_group.grb_angle_entry,
"gerber_editor_circ_dir": self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_dir_radio,
"gerber_editor_circ_angle":
self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_angle_entry,
"gerber_editor_scale_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_scale_entry,
"gerber_editor_buff_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_buff_entry,
"gerber_editor_ma_low": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_low_entry,
"gerber_editor_ma_high": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_high_entry,
# Excellon General
"excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
@ -558,6 +576,7 @@ class App(QtCore.QObject):
# NCC Tool
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
"tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry,
"tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry,
"tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_radio,
@ -578,6 +597,7 @@ class App(QtCore.QObject):
# Paint Area Tool
"tools_painttooldia": self.ui.tools_defaults_form.tools_paint_group.painttooldia_entry,
"tools_paintorder": self.ui.tools_defaults_form.tools_paint_group.paint_order_radio,
"tools_paintoverlap": self.ui.tools_defaults_form.tools_paint_group.paintoverlap_entry,
"tools_paintmargin": self.ui.tools_defaults_form.tools_paint_group.paintmargin_entry,
"tools_paintmethod": self.ui.tools_defaults_form.tools_paint_group.paintmethod_combo,
@ -681,8 +701,10 @@ class App(QtCore.QObject):
# Global APP Preferences
"global_serial": 0,
"global_stats": {},
"global_tabs_detachable": True,
"units": "IN",
"global_app_level": 'b',
"global_portable": False,
"global_language": 'English',
"global_version_check": True,
"global_send_stats": True,
@ -791,6 +813,20 @@ class App(QtCore.QObject):
# Gerber Editor
"gerber_editor_sel_limit": 30,
"gerber_editor_newcode": 10,
"gerber_editor_newsize": 0.8,
"gerber_editor_newtype": 'C',
"gerber_editor_newdim": "0.5, 0.5",
"gerber_editor_array_size": 5,
"gerber_editor_lin_axis": 'X',
"gerber_editor_lin_pitch": 1,
"gerber_editor_lin_angle": 0.0,
"gerber_editor_circ_dir": 'CW',
"gerber_editor_circ_angle": 0.0,
"gerber_editor_scale_f": 1.0,
"gerber_editor_buff_f": 0.1,
"gerber_editor_ma_low": 0.0,
"gerber_editor_ma_high": 1.0,
# Excellon General
"excellon_plot": True,
@ -916,6 +952,7 @@ class App(QtCore.QObject):
"cncjob_toolchange_macro_enable": False,
"tools_ncctools": "0.0393701, 0.019685",
"tools_nccorder": 'rev',
"tools_nccoverlap": 0.015748,
"tools_nccmargin": 0.0393701,
"tools_nccmethod": "seed",
@ -934,6 +971,7 @@ class App(QtCore.QObject):
"tools_cutout_convexshape": False,
"tools_painttooldia": 0.023622,
"tools_paintorder": 'rev',
"tools_paintoverlap": 0.015748,
"tools_paintmargin": 0.0,
"tools_paintmethod": "seed",
@ -1290,13 +1328,8 @@ class App(QtCore.QObject):
self.tools_form = None
self.on_options_combo_change(0) # Will show the initial form
# ### Define OBJECT COLLECTION ###
self.collection = ObjectCollection(self)
self.ui.project_tab_layout.addWidget(self.collection.view)
# ################################
self.log.debug("Finished creating Object Collection.")
# ### Initialize the color box's color in Preferences -> Global -> Color
# Init Plot Colors
self.ui.general_defaults_form.general_gui_group.pf_color_entry.set_value(self.defaults['global_plot_fill'])
@ -1368,46 +1401,52 @@ class App(QtCore.QObject):
"background-color:%s" % str(self.defaults['cncjob_annotation_fontcolor'])[:7])
# ### End of Data ####
# ### Plot Area ####
# ###############################################
# ############# SETUP Plot Area #################
# ###############################################
start_plot_time = time.time() # debug
self.plotcanvas = PlotCanvas(self.ui.right_layout, self)
self.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_over_plot)
self.plotcanvas.vis_connect('mouse_press', self.on_mouse_click_over_plot)
self.plotcanvas.vis_connect('mouse_release', self.on_mouse_click_release_over_plot)
self.plotcanvas.vis_connect('mouse_double_click', self.on_double_click_over_plot)
# Keys over plot enabled
self.plotcanvas.vis_connect('key_press', self.ui.keyPressEvent)
self.plotcanvas = None
self.app_cursor = None
self.hover_shapes = None
self.on_plotcanvas_setup()
end_plot_time = time.time()
self.log.debug("Finished Canvas initialization in %s seconds." % (str(end_plot_time - start_plot_time)))
self.ui.splitter.setStretchFactor(1, 2)
# So it can receive key presses
self.plotcanvas.vispy_canvas.native.setFocus()
self.app_cursor = self.plotcanvas.new_cursor()
self.app_cursor.enabled = False
# to use for tools like Measurement tool who depends on the event sources who are changed inside the Editors
# depending on from where those tools are called different actions can be done
self.call_source = 'app'
end_plot_time = time.time()
self.log.debug("Finished Canvas initialization in %s seconds." % (str(end_plot_time - start_plot_time)))
# ##############################################
# ######### SETUP OBJECT COLLECTION ############
# ##############################################
self.collection = ObjectCollection(self)
self.ui.project_tab_layout.addWidget(self.collection.view)
# ### Adjust tabs width ## ##
# self.collection.view.setMinimumWidth(self.ui.options_scroll_area.widget().sizeHint().width() +
# self.ui.options_scroll_area.verticalScrollBar().sizeHint().width())
self.collection.view.setMinimumWidth(290)
self.log.debug("Finished creating Object Collection.")
# ###############################################
# ############# Worker SETUP ####################
# ###############################################
# ### Worker ####
if self.defaults["global_worker_number"]:
self.workers = WorkerStack(workers_number=int(self.defaults["global_worker_number"]))
else:
self.workers = WorkerStack(workers_number=2)
self.worker_task.connect(self.workers.add_task)
self.log.debug("Finished creating Workers crew.")
# ################################################
# ############### Signal handling ################
# ################################################
# ### Signal handling ###
# ### Custom signals ###
self.inform.connect(self.info)
self.app_quit.connect(self.quit_application)
@ -1506,12 +1545,8 @@ class App(QtCore.QObject):
self.ui.menuviewenable.triggered.connect(self.enable_all_plots)
self.ui.menuview_zoom_fit.triggered.connect(self.on_zoom_fit)
self.ui.menuview_zoom_in.triggered.connect(
lambda: self.plotcanvas.zoom(1 / float(self.defaults['global_zoom_ratio']))
)
self.ui.menuview_zoom_out.triggered.connect(
lambda: self.plotcanvas.zoom(float(self.defaults['global_zoom_ratio']))
)
self.ui.menuview_zoom_in.triggered.connect(self.on_zoom_in)
self.ui.menuview_zoom_out.triggered.connect(self.on_zoom_out)
self.ui.menuview_toggle_code_editor.triggered.connect(self.on_toggle_code_editor)
self.ui.menuview_toggle_fscreen.triggered.connect(self.on_fullscreen)
@ -1545,6 +1580,15 @@ class App(QtCore.QObject):
# ToolBar signals
self.connect_toolbar_signals()
# Notebook signals
# make the right click on the notebook tab connect to a function
self.ui.notebook.setupContextMenu()
self.ui.notebook.addContextMenu(
_("Detachable Tabs"), self.on_notebook_tab_rmb_click,
initial_checked=self.defaults["global_tabs_detachable"])
# activate initial state
self.on_notebook_tab_rmb_click(self.defaults["global_tabs_detachable"])
# Context Menu
self.ui.popmenu_disable.triggered.connect(lambda: self.toggle_plots(self.collection.get_selected()))
self.ui.popmenu_panel_toggle.triggered.connect(self.on_toggle_notebook)
@ -1668,6 +1712,9 @@ class App(QtCore.QObject):
self.ui.buttonFind.clicked.connect(self.handleFindGCode)
self.ui.buttonReplace.clicked.connect(self.handleReplaceGCode)
# portability changed
self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked)
# Object list
self.collection.view.activated.connect(self.on_row_activated)
@ -1682,6 +1729,10 @@ class App(QtCore.QObject):
self.ui.excellon_options_form.excellon_opt_group.excellon_defaults_button.clicked.connect(
self.on_excellon_options_button)
# when there are arguments at application startup this get launched
self.args_at_startup.connect(self.on_startup_args)
self.log.debug("Finished connecting Signals.")
# this is a flag to signal to other tools that the ui tooltab is locked and not accessible
self.tool_tab_locked = False
@ -1691,21 +1742,18 @@ class App(QtCore.QObject):
else:
self.ui.splitter.setSizes([0, 1])
# ###################
# ### Other setups ##
# ###################
# ###########################################
# ################# Other setups ############
# ###########################################
# Sets up FlatCAMObj, FCProcess and FCProcessContainer.
self.setup_obj_classes()
self.setup_recent_items()
self.setup_component_editor()
# ############
# ### Shell ##
# ############
# #########################
# Auto-complete KEYWORDS ##
# #########################
# ###########################################
# #######Auto-complete KEYWORDS #############
# ###########################################
self.tcl_commands_list = ['add_circle', 'add_poly', 'add_polygon', 'add_polyline', 'add_rectangle',
'aligndrill', 'clear',
'aligndrillgrid', 'cncjob', 'cutout', 'delete', 'drillcncjob',
@ -1915,6 +1963,10 @@ class App(QtCore.QObject):
self.myKeywords = self.tcl_commands_list + self.ordinary_keywords + self.tcl_keywords
# ###########################################
# ########### Shell SETUP ###################
# ###########################################
self.shell = FCShell(self, version=self.version)
self.shell._edit.set_model_data(self.myKeywords)
self.ui.code_editor.set_model_data(self.myKeywords)
@ -1941,9 +1993,9 @@ class App(QtCore.QObject):
else:
self.ui.shell_dock.hide()
# ########################
# ### Tools and Plugins ##
# ########################
# ###########################################
# ######### Tools and Plugins ###############
# ###########################################
self.dblsidedtool = None
self.measurement_tool = None
@ -1969,6 +2021,10 @@ class App(QtCore.QObject):
# self.f_parse = ParseFont(self)
# self.parse_system_fonts()
# ###############################################
# ######## START-UP ARGUMENTS ###################
# ###############################################
# test if the program was started with a script as parameter
if self.cmd_line_shellfile:
try:
@ -1979,9 +2035,9 @@ class App(QtCore.QObject):
print("ERROR: ", ext)
sys.exit(2)
# ##########################
# ### Check for updates ####
# ##########################
# ###############################################
# ############# Check for updates ###############
# ###############################################
# Separate thread (Not worker)
# Check for updates on startup but only if the user consent and the app is not in Beta version
@ -1994,9 +2050,9 @@ class App(QtCore.QObject):
'params': []})
self.thr2.start(QtCore.QThread.LowPriority)
# ###################################
# ### Variables for global usage ####
# ###################################
# ################################################
# ######### Variables for global usage ###########
# ################################################
# coordinates for relative position display
self.rel_point1 = (0, 0)
@ -2061,13 +2117,13 @@ class App(QtCore.QObject):
self.dxf_list = ['dxf']
self.pdf_list = ['pdf']
self.prj_list = ['flatprj']
self.conf_list = ['flatconfig']
# global variable used by NCC Tool to signal that some polygons could not be cleared, if True
# flag for polygons not cleared
self.poly_not_cleared = False
# VisPy visuals
self.hover_shapes = ShapeCollection(parent=self.plotcanvas.vispy_canvas.view.scene, layers=1)
self.isHovering = False
self.notHovering = True
@ -2096,7 +2152,7 @@ class App(QtCore.QObject):
os.chmod(filename_factory, S_IREAD | S_IRGRP | S_IROTH)
####################################################
# ### EDITOR section ###############################
# ### ADDING FlatCAM EDITORS section ###############
####################################################
# watch out for the position of the editors instantiation ... if it is done before a save of the default values
@ -2113,50 +2169,13 @@ class App(QtCore.QObject):
App.log.debug("END of constructor. Releasing control.")
# accept a project file as command line parameter
self.set_ui_title(name=_("New Project - Not saved"))
# accept some type file as command line parameter: FlatCAM project, FlatCAM preferences or scripts
# the path/file_name must be enclosed in quotes if it contain spaces
for argument in App.args:
if '.FlatPrj' in argument:
try:
project_name = str(argument)
if App.args:
self.args_at_startup.emit()
if project_name == "":
self.inform.emit(_("Open cancelled."))
else:
# self.open_project(project_name)
run_from_arg = True
self.worker_task.emit({'fcn': self.open_project,
'params': [project_name, run_from_arg]})
except Exception as e:
log.debug("Could not open FlatCAM project file as App parameter due: %s" % str(e))
if '.FlatConfig' in argument:
try:
file_name = str(argument)
if file_name == "":
self.inform.emit(_("Open Config file failed."))
else:
# run_from_arg = True
# self.worker_task.emit({'fcn': self.open_config_file,
# 'params': [file_name, run_from_arg]})
self.open_config_file(file_name, run_from_arg=True)
except Exception as e:
log.debug("Could not open FlatCAM Config file as App parameter due: %s" % str(e))
if '.FlatScript' in argument:
try:
file_name = str(argument)
if file_name == "":
self.inform.emit(_("Open Script file failed."))
else:
# run_from_arg = True
# self.worker_task.emit({'fcn': self.open_script_file,
# 'params': [file_name, run_from_arg]})
self.on_filerunscript(name=file_name)
except Exception as e:
log.debug("Could not open FlatCAM Script file as App parameter due: %s" % str(e))
@staticmethod
def copy_and_overwrite(from_path, to_path):
@ -2175,6 +2194,52 @@ class App(QtCore.QObject):
from_new_path = os.path.dirname(os.path.realpath(__file__)) + '\\flatcamGUI\\VisPyData\\data'
shutil.copytree(from_new_path, to_path)
def on_startup_args(self):
log.debug("Application was started with an argument. Processing ...")
for argument in App.args:
if '.FlatPrj' in argument:
try:
project_name = str(argument)
if project_name == "":
self.inform.emit(_("Open cancelled."))
else:
# self.open_project(project_name)
run_from_arg = True
# self.worker_task.emit({'fcn': self.open_project,
# 'params': [project_name, run_from_arg]})
self.open_project(filename=project_name, run_from_arg=run_from_arg)
except Exception as e:
log.debug("Could not open FlatCAM project file as App parameter due: %s" % str(e))
elif '.FlatConfig' in argument:
try:
file_name = str(argument)
if file_name == "":
self.inform.emit(_("Open Config file failed."))
else:
# run_from_arg = True
# self.worker_task.emit({'fcn': self.open_config_file,
# 'params': [file_name, run_from_arg]})
self.open_config_file(file_name, run_from_arg=True)
except Exception as e:
log.debug("Could not open FlatCAM Config file as App parameter due: %s" % str(e))
elif '.FlatScript' in argument:
try:
file_name = str(argument)
if file_name == "":
self.inform.emit(_("Open Script file failed."))
else:
# run_from_arg = True
# self.worker_task.emit({'fcn': self.open_script_file,
# 'params': [file_name, run_from_arg]})
self.on_filerunscript(name=file_name)
except Exception as e:
log.debug("Could not open FlatCAM Script file as App parameter due: %s" % str(e))
def set_ui_title(self, name):
self.ui.setWindowTitle('FlatCAM %s %s - %s %s' %
(self.version,
@ -3006,7 +3071,7 @@ class App(QtCore.QObject):
# Save update options
try:
f = open(filename, "w")
json.dump(defaults_from_file, f)
json.dump(defaults_from_file, f, default=to_dict, indent=2, sort_keys=True)
f.close()
except:
self.inform.emit(_("[ERROR_NOTCL] Failed to write defaults to file."))
@ -3106,6 +3171,11 @@ class App(QtCore.QObject):
:param initialize: Function to run after creation of the object but before it is attached to the application.
The function is called with 2 parameters: the new object and the App instance.
:type initialize: function
:param active:
:param fit:
:param plot: If to plot the resulting object
:param autoselected: if the resulting object is autoselected in the Project tab and therefore in the
self.colleaction
:return: None
:rtype: None
"""
@ -3189,11 +3259,9 @@ class App(QtCore.QObject):
obj.options['ymin'] = ymin
obj.options['xmax'] = xmax
obj.options['ymax'] = ymax
except:
log.warning("The object has no bounds properties.")
# don't plot objects with no bounds, there is nothing to plot
self.plot = False
pass
except Exception as e:
log.warning("The object has no bounds properties. %s" % str(e))
return "fail"
FlatCAMApp.App.log.debug("Moving new object back to main thread.")
@ -3438,7 +3506,7 @@ class App(QtCore.QObject):
# log.debug("Application defaults saved ... Exit event.")
# QtWidgets.qApp.quit()
def save_defaults(self, silent=False):
def save_defaults(self, silent=False, data_path=None):
"""
Saves application default options
``self.defaults`` to current_defaults.FlatConfig.
@ -3447,9 +3515,12 @@ class App(QtCore.QObject):
"""
self.report_usage("save_defaults")
if data_path is None:
data_path = self.data_path
# Read options from file
try:
f = open(self.data_path + "/current_defaults.FlatConfig")
f = open(data_path + "/current_defaults.FlatConfig")
defaults_file_content = f.read()
f.close()
except:
@ -3506,7 +3577,7 @@ class App(QtCore.QObject):
# Save update options
try:
f = open(self.data_path + "/current_defaults.FlatConfig", "w")
f = open(data_path + "/current_defaults.FlatConfig", "w")
json.dump(defaults, f, default=to_dict, indent=2, sort_keys=True)
f.close()
except:
@ -3516,7 +3587,7 @@ class App(QtCore.QObject):
if not silent:
self.inform.emit(_("[success] Defaults saved."))
def save_factory_defaults(self, silent=False):
def save_factory_defaults(self, silent=False, data_path=None):
"""
Saves application factory default options
``self.defaults`` to factory_defaults.FlatConfig.
@ -3526,9 +3597,12 @@ class App(QtCore.QObject):
"""
self.report_usage("save_factory_defaults")
if data_path is None:
data_path = self.data_path
# Read options from file
try:
f_f_def = open(self.data_path + "/factory_defaults.FlatConfig")
f_f_def = open(data_path + "/factory_defaults.FlatConfig")
factory_defaults_file_content = f_f_def.read()
f_f_def.close()
except:
@ -3554,7 +3628,7 @@ class App(QtCore.QObject):
# Save update options
try:
f_f_def_s = open(self.data_path + "/factory_defaults.FlatConfig", "w")
f_f_def_s = open(data_path + "/factory_defaults.FlatConfig", "w")
json.dump(factory_defaults, f_f_def_s, default=to_dict, indent=2, sort_keys=True)
f_f_def_s.close()
except:
@ -3608,11 +3682,96 @@ class App(QtCore.QObject):
settings.setValue('axis_font_size',
self.ui.general_defaults_form.general_gui_set_group.axis_font_size_spinner.get_value())
settings.setValue('toolbar_lock', self.ui.lock_action.isChecked())
# This will write the setting to the platform specific storage.
del settings
log.debug("App.final_save() --> App UI state saved.")
QtWidgets.qApp.quit()
def on_portable_checked(self, state):
line_no = 0
data = None
if sys.platform != 'win32':
# this won't work in Linux or MacOS
return
# test if the app was frozen and choose the path for the configuration file
if getattr(sys, "frozen", False) is True:
current_data_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\config'
else:
current_data_path = os.path.dirname(os.path.realpath(__file__)) + '\\config'
config_file = current_data_path + '\\configuration.txt'
try:
with open(config_file, 'r') as f:
try:
data = f.readlines()
except Exception as e:
log.debug('App.__init__() -->%s' % str(e))
return
except FileNotFoundError:
pass
for line in data:
line = line.strip('\n')
param = str(line).rpartition('=')
if param[0] == 'portable':
break
line_no += 1
if state:
data[line_no] = 'portable=True\n'
# create the new defauults files
# create current_defaults.FlatConfig file if there is none
try:
f = open(current_data_path + '/current_defaults.FlatConfig')
f.close()
except IOError:
App.log.debug('Creating empty current_defaults.FlatConfig')
f = open(current_data_path + '/current_defaults.FlatConfig', 'w')
json.dump({}, f)
f.close()
# create factory_defaults.FlatConfig file if there is none
try:
f = open(current_data_path + '/factory_defaults.FlatConfig')
f.close()
except IOError:
App.log.debug('Creating empty factory_defaults.FlatConfig')
f = open(current_data_path + '/factory_defaults.FlatConfig', 'w')
json.dump({}, f)
f.close()
try:
f = open(current_data_path + '/recent.json')
f.close()
except IOError:
App.log.debug('Creating empty recent.json')
f = open(current_data_path + '/recent.json', 'w')
json.dump([], f)
f.close()
try:
fp = open(current_data_path + '/recent_projects.json')
fp.close()
except IOError:
App.log.debug('Creating empty recent_projects.json')
fp = open(current_data_path + '/recent_projects.json', 'w')
json.dump([], fp)
fp.close()
# save the current defaults to the new defaults file
self.save_defaults(silent=True, data_path=current_data_path)
self.save_factory_defaults(silent=True, data_path=current_data_path)
else:
data[line_no] = 'portable=False\n'
with open(config_file, 'w') as f:
f.writelines(data)
def on_toggle_shell(self):
"""
toggle shell if is visible close it if closed open it
@ -4010,7 +4169,7 @@ class App(QtCore.QObject):
msgbox = QtWidgets.QMessageBox()
msgbox.setWindowTitle(_("Toggle Units"))
msgbox.setWindowIcon(QtGui.QIcon('share/toggle_units32.png'))
msgbox.setText(_("<B>Change project units ...</B>"))
msgbox.setText("<B>%s</B>" % _("Change project units ..."))
msgbox.setInformativeText(_("Changing the units of the project causes all geometrical "
"properties of all objects to be scaled accordingly.\nContinue?"))
bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole)
@ -4545,6 +4704,13 @@ class App(QtCore.QObject):
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
def on_notebook_tab_rmb_click(self, checked):
self.ui.notebook.set_detachable(val=checked)
self.defaults["global_tabs_detachable"] = checked
self.ui.plot_tab_area.set_detachable(val=checked)
self.defaults["global_tabs_detachable"] = checked
def on_deselect_all(self):
self.collection.set_all_inactive()
self.delete_selection_shape()
@ -4990,7 +5156,7 @@ class App(QtCore.QObject):
msgbox = QtWidgets.QMessageBox()
msgbox.setWindowTitle(_("Delete objects"))
msgbox.setWindowIcon(QtGui.QIcon('share/deleteshape32.png'))
# msgbox.setText(_("<B>Delete FlatCAM objects ...</B>"))
# msgbox.setText("<B>%s</B>" % _("Change project units ..."))
msgbox.setText(_("Are you sure you want to permanently delete\n"
"the selected objects?"))
bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole)
@ -7166,12 +7332,15 @@ class App(QtCore.QObject):
self.should_we_save = False
def on_file_saveprojectas(self, make_copy=False, thread=True, quit=False):
def on_file_saveprojectas(self, make_copy=False, use_thread=True, quit_action=False):
"""
Callback for menu item File->Save Project As... Opens a file
chooser and saves the project to the given file via
``self.save_project()``.
:param make_copy if to be create a copy of the project; boolean
:param use_thread: if to be run in a separate thread; boolean
:param quit_action: if to be followed by quiting the application; boolean
:return: None
"""
@ -7202,16 +7371,17 @@ class App(QtCore.QObject):
except IOError:
pass
if thread is True:
if use_thread is True:
self.worker_task.emit({'fcn': self.save_project,
'params': [filename, quit]})
'params': [filename, quit_action]})
else:
self.save_project(filename, quit)
self.save_project(filename, quit_action)
# self.save_project(filename)
if self.defaults["global_open_style"] is False:
self.file_opened.emit("project", filename)
self.file_saved.emit("project", filename)
if not make_copy:
self.project_filename = filename
@ -7222,7 +7392,9 @@ class App(QtCore.QObject):
"""
Exports a Geometry Object to an SVG file.
:param obj_name: the name of the FlatCAM object to be saved as SVG
:param filename: Path to the SVG file to save to.
:param scale_factor: factor by which to change/scale the thickness of the features
:return:
"""
self.report_usage("export_svg()")
@ -7284,9 +7456,12 @@ class App(QtCore.QObject):
"""
Exports a Geometry Object to an SVG file in negative.
:param obj_name: the name of the FlatCAM object to be saved as SVG
:param box_name: the name of the FlatCAM object to be used as delimitation of the content to be saved
:param filename: Path to the SVG file to save to.
:param: use_thread: If True use threads
:type: Bool
:param boundary: thickness of a black border to surround all the features
:param scale_factor: factor by which to change/scale the thickness of the features
:param use_thread: if to be run in a separate thread; boolean
:return:
"""
self.report_usage("export_negative()")
@ -7408,11 +7583,13 @@ class App(QtCore.QObject):
def export_svg_black(self, obj_name, box_name, filename, scale_factor=0.00, use_thread=True):
"""
Exports a Geometry Object to an SVG file in negative.
Exports a Geometry Object to an SVG file in positive black.
:param obj_name: the name of the FlatCAM object to be saved as SVG
:param box_name: the name of the FlatCAM object to be used as delimitation of the content to be saved
:param filename: Path to the SVG file to save to.
:param: use_thread: If True use threads
:type: Bool
:param scale_factor: factor by which to change/scale the thickness of the features
:param use_thread: if to be run in a separate thread; boolean
:return:
"""
self.report_usage("export_svg_black()")
@ -7529,9 +7706,11 @@ class App(QtCore.QObject):
def save_source_file(self, obj_name, filename, use_thread=True):
"""
Exports a Gerber Object to an Gerber file.
Exports a FlatCAM Object to an Gerber/Excellon file.
:param obj_name: the name of the FlatCAM object for which to save it's embedded source file
:param filename: Path to the Gerber file to save to.
:param use_thread: if to be run in a separate thread
:return:
"""
self.report_usage("save source file()")
@ -7565,7 +7744,9 @@ class App(QtCore.QObject):
"""
Exports a Excellon Object to an Excellon file.
:param obj_name: the name of the FlatCAM object to be saved as Excellon
:param filename: Path to the Excellon file to save to.
:param use_thread: if to be run in a separate thread
:return:
"""
self.report_usage("export_excellon()")
@ -7701,7 +7882,9 @@ class App(QtCore.QObject):
"""
Exports a Gerber Object to an Gerber file.
:param obj_name: the name of the FlatCAM object to be saved as Gerber
:param filename: Path to the Gerber file to save to.
:param use_thread: if to be run in a separate thread
:return:
"""
self.report_usage("export_gerber()")
@ -7824,7 +8007,9 @@ class App(QtCore.QObject):
"""
Exports a Geometry Object to an DXF file.
:param obj_name: the name of the FlatCAM object to be saved as DXF
:param filename: Path to the DXF file to save to.
:param use_thread: if to be run in a separate thread
:return:
"""
self.report_usage("export_dxf()")
@ -7868,7 +8053,7 @@ class App(QtCore.QObject):
def job_thread_exc(app_obj):
ret = make_dxf()
if ret == 'fail':
self.inform.emit(_('[[WARNING_NOTCL]] Could not export DXF file.'))
app_obj.inform.emit(_('[[WARNING_NOTCL]] Could not export DXF file.'))
return
self.worker_task.emit({'fcn': job_thread_exc, 'params': [self]})
@ -8254,6 +8439,8 @@ class App(QtCore.QObject):
"""
App.log.debug("Opening project: " + filename)
self.set_ui_title(name=_("Loading Project ... Please Wait ..."))
# Open and parse an uncompressed Project file
try:
f = open(filename, 'r')
@ -8293,20 +8480,26 @@ class App(QtCore.QObject):
self.set_screen_units(self.options["units"])
# Re create objects
App.log.debug("Re-creating objects...")
App.log.debug(" **************** Started PROEJCT loading... **************** ")
for obj in d['objs']:
def obj_init(obj_inst, app_inst):
obj_inst.from_dict(obj)
App.log.debug(obj['kind'] + ": " + obj['options']['name'])
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']))
self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=True)
self.plot_all()
# self.plot_all()
self.inform.emit(_("[success] Project loaded from: %s") % filename)
self.should_we_save = False
self.file_opened.emit("project", filename)
self.set_ui_title(name=self.project_filename)
App.log.debug("Project loaded")
App.log.debug(" **************** Finished PROJECT loading... **************** ")
def propagate_defaults(self, silent=False):
"""
@ -8856,6 +9049,35 @@ The normal flow when working in FlatCAM is the following:</span></p>
_("info")
)
def on_plotcanvas_setup(self, container=None):
"""
This is doing the setup for the plot area (VisPy canvas)
:param container: widget where to install the canvas
:return: None
"""
if container:
plot_container = container
else:
plot_container = self.ui.right_layout
self.plotcanvas = PlotCanvas(plot_container, self)
# So it can receive key presses
self.plotcanvas.vispy_canvas.native.setFocus()
self.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_over_plot)
self.plotcanvas.vis_connect('mouse_press', self.on_mouse_click_over_plot)
self.plotcanvas.vis_connect('mouse_release', self.on_mouse_click_release_over_plot)
self.plotcanvas.vis_connect('mouse_double_click', self.on_double_click_over_plot)
# Keys over plot enabled
self.plotcanvas.vis_connect('key_press', self.ui.keyPressEvent)
self.app_cursor = self.plotcanvas.new_cursor()
self.app_cursor.enabled = False
self.hover_shapes = ShapeCollection(parent=self.plotcanvas.vispy_canvas.view.scene, layers=1)
def on_zoom_fit(self, event):
"""
Callback for zoom-out request. This can be either from the corresponding
@ -8868,6 +9090,12 @@ The normal flow when working in FlatCAM is the following:</span></p>
self.plotcanvas.fit_view()
def on_zoom_in(self):
self.plotcanvas.zoom(1 / float(self.defaults['global_zoom_ratio']))
def on_zoom_out(self):
self.plotcanvas.zoom(float(self.defaults['global_zoom_ratio']))
def disable_all_plots(self):
self.report_usage("disable_all_plots()")
@ -8906,7 +9134,6 @@ The normal flow when working in FlatCAM is the following:</span></p>
:param objects: list of Objects to be enabled
:return:
"""
log.debug("Enabling plots ...")
self.inform.emit(_("Working ..."))
for obj in objects:
@ -8970,12 +9197,13 @@ The normal flow when working in FlatCAM is the following:</span></p>
for obj in objects:
obj.on_generatecnc_button_click()
def save_project(self, filename, quit=False):
def save_project(self, filename, quit_action=False):
"""
Saves the current project to the specified file.
:param filename: Name of the file in which to save.
:type filename: str
:param quit_action: if the project saving will be followed by an app quit; boolean
:return: None
"""
self.log.debug("save_project()")
@ -9037,28 +9265,37 @@ The normal flow when working in FlatCAM is the following:</span></p>
else:
self.inform.emit(_("[ERROR_NOTCL] Failed to save project file: %s. Retry to save it.") % filename)
settings = QSettings("Open Source", "FlatCAM")
lock_state = self.ui.lock_action.isChecked()
settings.setValue('toolbar_lock', lock_state)
# This will write the setting to the platform specific storage.
del settings
# if quit:
# t = threading.Thread(target=lambda: self.check_project_file_size(1, filename=filename))
# t.start()
self.start_delayed_quit(delay=500, filename=filename, quit=quit)
self.start_delayed_quit(delay=500, filename=filename, should_quit=quit_action)
def start_delayed_quit(self, delay, filename, quit=None):
def start_delayed_quit(self, delay, filename, should_quit=None):
"""
:param delay: period of checking if project file size is more than zero; in seconds
:param filename: the name of the project file to be checked periodically for size more than zero
:param should_quit: if the task finished will be followed by an app quit; boolean
:return:
"""
to_quit = quit
to_quit = should_quit
self.save_timer = QtCore.QTimer()
self.save_timer.setInterval(delay)
self.save_timer.timeout.connect(lambda: self.check_project_file_size(filename=filename, quit=to_quit))
self.save_timer.timeout.connect(lambda: self.check_project_file_size(filename=filename, should_quit=to_quit))
self.save_timer.start()
def check_project_file_size(self, filename, quit=None):
def check_project_file_size(self, filename, should_quit=None):
"""
:param filename: the name of the project file to be checked periodically for size more than zero
:param should_quit: will quit the app if True; boolean
:return:
"""
@ -9066,9 +9303,9 @@ The normal flow when working in FlatCAM is the following:</span></p>
if os.stat(filename).st_size > 0:
self.save_in_progress = False
self.save_timer.stop()
if quit:
if should_quit:
self.app_quit.emit()
except Exception:
except Exception as e:
traceback.print_exc()
def on_options_app2project(self):

View File

@ -136,7 +136,6 @@ class FlatCAMObj(QtCore.QObject):
def on_options_change(self, key):
# Update form on programmatically options change
self.set_form_item(key)
# Set object visibility
if key == 'plot':
self.visible = self.options['plot']
@ -774,7 +773,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
if self.ui.follow_cb.get_value() is True:
obj = self.app.collection.get_active()
obj.follow()
obj.follow_geo()
# in the end toggle the visibility of the origin object so we can see the generated Geometry
obj.ui.plot_cb.toggle()
else:
@ -786,7 +785,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
if self.ui.follow_cb.get_value() is True:
obj = self.app.collection.get_active()
obj.follow()
obj.follow_geo()
# in the end toggle the visibility of the origin object so we can see the generated Geometry
obj.ui.plot_cb.toggle()
else:
@ -1130,6 +1129,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
:return: None
:rtype: None
"""
log.debug("FlatCAMObj.FlatCAMGerber.convert_units()")
factor = Gerber.convert_units(self, units)
@ -2769,8 +2769,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def convert_units(self, units):
factor = Excellon.convert_units(self, units)
log.debug("FlatCAMObj.FlatCAMExcellon.convert_units()")
factor = Excellon.convert_units(self, units)
self.options['drillz'] = float(self.options['drillz']) * factor
self.options['travelz'] = float(self.options['travelz']) * factor
self.options['feedrate'] = float(self.options['feedrate']) * factor
@ -3423,7 +3424,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.level.setText(_(
'<span style="color:red;"><b>Advanced</b></span>'
))
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
self.ui.paint_tool_button.clicked.connect(lambda: self.app.paint_tool.run(toggle=False))
@ -4958,6 +4958,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
:return: None
:rtype: None
"""
log.debug("FlatCAMObj.FlatCAMGeometry.scale()")
try:
xfactor = float(xfactor)
@ -5027,6 +5028,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
:return: None
:rtype: None
"""
log.debug("FlatCAMObj.FlatCAMGeometry.offset()")
try:
dx, dy = vect
@ -5057,6 +5059,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.app.inform.emit(_("[success] Geometry Offset done."))
def convert_units(self, units):
log.debug("FlatCAMObj.FlatCAMGeometry.convert_units()")
self.ui_disconnect()
factor = Geometry.convert_units(self, units)
@ -5203,11 +5207,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
for tooluid_key in self.tools:
solid_geometry = self.tools[tooluid_key]['solid_geometry']
self.plot_element(solid_geometry, visible=visible)
# plot solid geometry that may be an direct attribute of the geometry object
# for SingleGeo
if self.solid_geometry:
self.plot_element(self.solid_geometry, visible=visible)
else:
# plot solid geometry that may be an direct attribute of the geometry object
# for SingleGeo
if self.solid_geometry:
self.plot_element(self.solid_geometry, visible=visible)
# self.plot_element(self.solid_geometry, visible=self.options['plot'])
self.shapes.redraw()
@ -5217,8 +5221,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
def on_plot_cb_click(self, *args):
if self.muted_ui:
return
self.plot()
self.read_form_item('plot')
self.plot()
self.ui_disconnect()
cb_flag = self.ui.plot_cb.isChecked()
@ -6024,8 +6028,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
self.annotation.redraw()
def convert_units(self, units):
log.debug("FlatCAMObj.FlatCAMECNCjob.convert_units()")
factor = CNCjob.convert_units(self, units)
FlatCAMApp.App.log.debug("FlatCAMCNCjob.convert_units()")
self.options["tooldia"] = float(self.options["tooldia"]) * factor
param_list = ['cutz', 'depthperpass', 'travelz', 'feedrate', 'feedrate_z', 'feedrate_rapid',

View File

@ -7,6 +7,7 @@
# ########################################################## ##
from PyQt5 import QtCore
# import traceback
class Worker(QtCore.QObject):
@ -60,6 +61,7 @@ class Worker(QtCore.QObject):
task['fcn'](*task['params'])
except Exception as e:
self.app.thread_exception.emit(e)
# print(traceback.format_exc())
# raise e
finally:
self.task_completed.emit(self.name)

View File

@ -693,7 +693,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
:param name: Name of the FlatCAM Object
:return: None
"""
log.debug("ObjectCollection.set_inactive()")
# log.debug("ObjectCollection.set_inactive()")
obj = self.get_by_name(name)
item = obj.item

View File

@ -9,6 +9,74 @@ CAD program, and create G-Code for Isolation routing.
=================================================
23.08.2019
- in Tool Cutout for the manual gaps, right mouse button click will exit from the action of adding gaps
- in Tool Cutout tool I've added the possibility to create a cutout without bridge gaps; added the 'None' option in the Gaps combobox
- in NCC Tool added ability to add multiple zones to clear when Area option is checked and the modifier key is pressed (either CTRL or SHIFT as set in Preferences). Right click of the mouse is an additional way to finish the job.
- fixed a bug in Excellon Editor that made that the selection of drills is always cumulative
- in Paint Tool added ability to add multiple zones to paint when Area option is checked and the modifier key is pressed (either CTRL or SHIFT as set in Preferences). Right click of the mouse is an additional way to finish the job.
- in Paint Tool and NCC Tool, for the Area option, now mouse panning is allowed while adding areas to process
- for all the FlatCAM tools launched from toolbar the behavior is modified: first click it will launch the tool; second click: if the Tool tab has focus it will close the tool but if another tab is selected, the tool will have focus
- modified the NCC Tool and Paint Tool to work multiple times after first launch
- fixed the issue with GUI entries content being deselected on right click in the box in order to copy the value
- some changes in GUI tooltips
- modified the way key modifiers are detected in Gerber Editor Selection class and in Excellon Editor Selection class
- updated the translations
- fixed aperture move in Gerber Editor
- fixed drills/slots move in Excellon Editor
- RELEASE 8.96
22.08.2019
- added ability to turn ON/OFF the detachable capability of the tabs in Notebook through a context menu activated by right mouse button click on the Notebook header
- added ability to turn ON/OFF the detachable capability of the tabs in Plot Tab Area through a context menu activated by right mouse button click on the Notebook header
- added possibility to turn application portable from the Edit -> Preferences -> General -> App. Preferences -> Portable checkbox
- moved the canvas setup into it's own function and called it in the init() function
- fixed the Buffer Tool in Geometry Editor; made the Buffer entry field a QDoubleSpinner and set the lower limit to zero.
- fixed Tool Cutout so when the target Gerber is a single Polygon then the created manual geometry will follow the shape if shape is freeform
- fixed TclCommandFollow command; an older function name was used who yielded wrong results
- in Tool Cutout for the manual gaps, now the moving geometry that cuts gaps will orient itself to fit the angle of the cutout geometry
21.08.2019
- added feature in Paint Tool allowing the painting to be done on Gerber objects
- added feature in Paint Tool to set how (and if) the tools are sorted
- added Edit -> Preferences GUI entries for the above just added features
- added new entry in Properties Tool which is the calculated Convex Hull Area (should give a more precise area for the irregular shapes than the box area)
- added some more strings in Properties Tool for the translation
- in NCC Tool added area selection feature
- fixed bug in Excellon parser for the Excellon files that do not put the type of zero suppression they use in the file (like DipTrace eCAD)
- fixed some issues introduced in NCC Tool
20.08.2019
- added ability to do copper clearing through NCC Tool on Geometry objects
- replaced the layout from Grid to Form for the Reference objects comboboxes in Paint Tool and in NCC Tool
19.08.2019
- updated the Edit -> Preferences to include also the Gerber Editor complete Preferences
- started to update the app strings to make it easier for future translations
- fixed the POT file and the German translation
- some mods in the Tool Sub
- fixed bug in Tool Sub that created issues when toggling visibility of the plots
- fixed the Spanish, Brazilian Portuguese and Romanian translations
18.08.2019
- made the exported preferences formatted therefore more easily read
- projects at startup don't work in another thread so there is no multithreading if I want to double click an project and to load it
- added messages in the application window title which show the progress in loading a project (which is not thread-safe therefore keeping the app from fully initialize until finished)
- in NCC Tool added a new parameter (radio button) that offer the choice on the order of the tools both in tools table and in execution of engraving; added as a parameter also in Edit -> Preferences -> Tools -> NCC Tool
- added possibility to drag & drop FlatCAM config files (*.FlatConfig) into the canvas to be opened into the application
- added GUI in Paint tool in beginning to add Paint by external reference object
- finished adding in Paint Tool the usage of an external object to set the extent of th area painted. For simple shapes (single Polygon) the shape can be anything, for the rest will be a convex hull of the reference object
- modified NCC tool so for simple objects (single Polygon) the external object used as reference can have any shape, for the other types of objects the copper cleared area will be the convex hull of the reference object
- modified the strings of the app wherever they contained the char seq <b> </b> so it is not included in the translated string
- updated the translation files for the modified strings (and for the newly added strings)
- added ability to lock toolbars within the context menu that is popped up on any toolbars right mouse click. The value is saved in QSettings and it is persistent between application startup's.
17.08.2019
- added estimated time of routing for the CNCJob and added travelled distance parameter for geometry, too

View File

@ -229,7 +229,8 @@ class Geometry(object):
# fixed issue of getting bounds only for one level lists of objects
# now it can get bounds for nested lists of objects
log.debug("Geometry->bounds()")
log.debug("camlib.Geometry.bounds()")
if self.solid_geometry is None:
log.debug("solid_geometry is None")
return 0, 0, 0, 0
@ -1278,7 +1279,7 @@ class Geometry(object):
:return: Scaling factor resulting from unit change.
:rtype: float
"""
log.debug("Geometry.convert_units()")
log.debug("camlib.Geometry.convert_units()")
if units.upper() == self.units.upper():
return 1.0
@ -1378,6 +1379,7 @@ class Geometry(object):
:type point: list
:return: None
"""
log.debug("camlib.Geometry.mirror()")
px, py = point
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
@ -1420,6 +1422,7 @@ class Geometry(object):
See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations
"""
log.debug("camlib.Geometry.rotate()")
px, py = point
@ -1460,6 +1463,8 @@ class Geometry(object):
See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations
"""
log.debug("camlib.Geometry.skew()")
px, py = point
def skew_geom(obj):
@ -3298,7 +3303,8 @@ class Gerber (Geometry):
# fixed issue of getting bounds only for one level lists of objects
# now it can get bounds for nested lists of objects
log.debug("Gerber->bounds()")
log.debug("camlib.Gerber.bounds()")
if self.solid_geometry is None:
log.debug("solid_geometry is None")
return 0, 0, 0, 0
@ -3442,6 +3448,8 @@ class Gerber (Geometry):
:type vect: tuple
:return: None
"""
log.debug("camlib.Gerber.offset()")
try:
dx, dy = vect
except TypeError:
@ -3504,6 +3512,7 @@ class Gerber (Geometry):
:type point: list
:return: None
"""
log.debug("camlib.Gerber.mirror()")
px, py = point
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
@ -3554,6 +3563,7 @@ class Gerber (Geometry):
See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations
"""
log.debug("camlib.Gerber.skew()")
px, py = point
@ -3596,6 +3606,7 @@ class Gerber (Geometry):
:param point:
:return:
"""
log.debug("camlib.Gerber.rotate()")
px, py = point
@ -4521,7 +4532,7 @@ class Excellon(Geometry):
else:
result = float(number_str) / (10 ** (float(nr_length) - float(self.excellon_format_upper_mm)))
return result
elif self.zeros == "T" or self.zeros == "TZ": # Trailing
else: # Trailing
# You must show all zeros to the right of the number and can omit
# all zeros to the left of the number. The CNC-7 will count the number
# of digits you typed and automatically fill in the missing zeros.
@ -4533,9 +4544,6 @@ class Excellon(Geometry):
else: # Metric is 000.000
result = float(number_str) / (10 ** (float(self.excellon_format_lower_mm)))
return result
else: # None - the numbers are in decimal format
return float(number_str)
except Exception as e:
log.error("Aborted. Operation could not be completed due of %s" % str(e))
return
@ -4631,7 +4639,7 @@ class Excellon(Geometry):
# fixed issue of getting bounds only for one level lists of objects
# now it can get bounds for nested lists of objects
log.debug("Excellon() -> bounds()")
log.debug("camlib.Excellon.bounds()")
# if self.solid_geometry is None:
# log.debug("solid_geometry is None")
# return 0, 0, 0, 0
@ -4691,6 +4699,8 @@ class Excellon(Geometry):
:type str: IN or MM
:return:
"""
log.debug("camlib.Excellon.convert_units()")
factor = Geometry.convert_units(self, units)
# Tools
@ -4711,6 +4721,8 @@ class Excellon(Geometry):
:return: None
:rtype: NOne
"""
log.debug("camlib.Excellon.scale()")
if yfactor is None:
yfactor = xfactor
@ -4755,6 +4767,7 @@ class Excellon(Geometry):
:type vect: tuple
:return: None
"""
log.debug("camlib.Excellon.offset()")
dx, dy = vect
@ -4795,6 +4808,8 @@ class Excellon(Geometry):
:type point: list
:return: None
"""
log.debug("camlib.Excellon.mirror()")
px, py = point
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
@ -4842,6 +4857,8 @@ class Excellon(Geometry):
See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations
"""
log.debug("camlib.Excellon.skew()")
if angle_x is None:
angle_x = 0.0
@ -4900,6 +4917,7 @@ class Excellon(Geometry):
:param point: tuple of coordinates (x, y)
:return:
"""
log.debug("camlib.Excellon.rotate()")
def rotate_geom(obj, origin=None):
if type(obj) is list:
@ -5092,8 +5110,9 @@ class CNCjob(Geometry):
return self.__dict__
def convert_units(self, units):
log.debug("camlib.CNCJob.convert_units()")
factor = Geometry.convert_units(self, units)
log.debug("CNCjob.convert_units()")
self.z_cut = float(self.z_cut) * factor
self.z_move *= factor
@ -6987,6 +7006,8 @@ class CNCjob(Geometry):
# fixed issue of getting bounds only for one level lists of objects
# now it can get bounds for nested lists of objects
log.debug("camlib.CNCJob.bounds()")
def bounds_rec(obj):
if type(obj) is list:
minx = Inf
@ -7058,6 +7079,7 @@ class CNCjob(Geometry):
:return: None
:rtype: None
"""
log.debug("camlib.CNCJob.scale()")
if yfactor is None:
yfactor = xfactor
@ -7207,6 +7229,8 @@ class CNCjob(Geometry):
:type vect: tuple
:return: None
"""
log.debug("camlib.CNCJob.offset()")
dx, dy = vect
def offset_g(g):
@ -7271,6 +7295,8 @@ class CNCjob(Geometry):
:param point: tupple of coordinates (x,y)
:return:
"""
log.debug("camlib.CNCJob.mirror()")
px, py = point
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
@ -7296,6 +7322,8 @@ class CNCjob(Geometry):
See shapely manual for more information:
http://toblerity.org/shapely/manual.html#affine-transformations
"""
log.debug("camlib.CNCJob.skew()")
px, py = point
for g in self.gcode_parsed:
@ -7312,6 +7340,7 @@ class CNCjob(Geometry):
:param point: tupple of coordinates (x,y)
:return:
"""
log.debug("camlib.CNCJob.rotate()")
px, py = point

View File

@ -1 +1 @@
portable=False
portable=False

View File

@ -1251,6 +1251,9 @@ class FCDrillSelect(DrawTool):
self.storage = self.exc_editor_app.storage_dict
# self.selected = self.exc_editor_app.selected
# here we store the selected tools
self.sel_tools = set()
# here we store all shapes that were selected so we can search for the nearest to our click location
self.sel_storage = FlatCAMExcEditor.make_storage()
@ -1261,16 +1264,18 @@ class FCDrillSelect(DrawTool):
def click(self, point):
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if self.exc_editor_app.app.defaults["global_mselect_key"] == 'Control':
if key_modifier == Qt.ControlModifier:
pass
else:
self.exc_editor_app.selected = []
if key_modifier == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif key_modifier == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
if key_modifier == Qt.ShiftModifier:
pass
else:
self.exc_editor_app.selected = []
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
pass
else:
self.exc_editor_app.selected = []
def click_release(self, pos):
self.exc_editor_app.tools_table_exc.clearSelection()
@ -1308,11 +1313,13 @@ class FCDrillSelect(DrawTool):
self.exc_editor_app.selected = []
else:
modifiers = QtWidgets.QApplication.keyboardModifiers()
mod_key = 'Control'
if modifiers == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif modifiers == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
if closest_shape in self.exc_editor_app.selected:
@ -1329,14 +1336,13 @@ class FCDrillSelect(DrawTool):
except (TypeError, AttributeError):
pass
sel_tools = set()
self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
for shape_s in self.exc_editor_app.selected:
for storage in self.exc_editor_app.storage_dict:
if shape_s in self.exc_editor_app.storage_dict[storage].get_objects():
sel_tools.add(storage)
self.sel_tools.add(storage)
for storage in sel_tools:
for storage in self.sel_tools:
for k, v in self.draw_app.tool2tooldia.items():
if v == storage:
self.exc_editor_app.tools_table_exc.selectRow(int(k) - 1)

View File

@ -76,7 +76,9 @@ class BufferSelectionTool(FlatCAMTool):
self.buffer_tools_box.addLayout(form_layout)
# Buffer distance
self.buffer_distance_entry = FCEntry()
self.buffer_distance_entry = FCDoubleSpinner()
self.buffer_distance_entry.set_precision(4)
self.buffer_distance_entry.set_range(0.0000, 999999.9999)
form_layout.addRow(_("Buffer distance:"), self.buffer_distance_entry)
self.buffer_corner_lbl = QtWidgets.QLabel(_("Buffer corner:"))
self.buffer_corner_lbl.setToolTip(
@ -2358,10 +2360,6 @@ class FCSelect(DrawTool):
def click_release(self, point):
self.select_shapes(point)
return ""
def select_shapes(self, pos):
# list where we store the overlapped shapes under our mouse left click position
over_shape_list = []
@ -2381,7 +2379,7 @@ class FCSelect(DrawTool):
# 3rd method of click selection -> inconvenient
try:
_, closest_shape = self.storage.nearest(pos)
_, closest_shape = self.storage.nearest(point)
except StopIteration:
return ""
@ -2400,30 +2398,28 @@ class FCSelect(DrawTool):
obj_to_add = over_shape_list[int(FlatCAMGeoEditor.draw_shape_idx)]
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if self.draw_app.app.defaults["global_mselect_key"] == 'Control':
# if CONTROL key is pressed then we add to the selected list the current shape but if it's already
if key_modifier == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif key_modifier == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
# if modifier key is pressed then we add to the selected list the current shape but if it's already
# in the selected list, we removed it. Therefore first click selects, second deselects.
if key_modifier == Qt.ControlModifier:
if obj_to_add in self.draw_app.selected:
self.draw_app.selected.remove(obj_to_add)
else:
self.draw_app.selected.append(obj_to_add)
if obj_to_add in self.draw_app.selected:
self.draw_app.selected.remove(obj_to_add)
else:
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
else:
if key_modifier == Qt.ShiftModifier:
if obj_to_add in self.draw_app.selected:
self.draw_app.selected.remove(obj_to_add)
else:
self.draw_app.selected.append(obj_to_add)
else:
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
except Exception as e:
log.error("[ERROR] Something went bad. %s" % str(e))
raise
return ""
class FCMove(FCShapeTool):
@ -2708,11 +2704,13 @@ class FCBuffer(FCShapeTool):
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
join_style = self.buff_tool.buffer_corner_cb.currentIndex() + 1
self.draw_app.buffer(buffer_distance, join_style)
ret_val = self.draw_app.buffer(buffer_distance, join_style)
self.app.ui.notebook.setTabText(2, _("Tools"))
self.draw_app.app.ui.splitter.setSizes([0, 1])
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit(_("[success] Done. Buffer Tool completed."))
def on_buffer_int(self):
@ -2734,11 +2732,13 @@ class FCBuffer(FCShapeTool):
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
join_style = self.buff_tool.buffer_corner_cb.currentIndex() + 1
self.draw_app.buffer_int(buffer_distance, join_style)
ret_val = self.draw_app.buffer_int(buffer_distance, join_style)
self.app.ui.notebook.setTabText(2, _("Tools"))
self.draw_app.app.ui.splitter.setSizes([0, 1])
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit(_("[success] Done. Buffer Int Tool completed."))
def on_buffer_ext(self):
@ -2760,11 +2760,13 @@ class FCBuffer(FCShapeTool):
# the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
# I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
join_style = self.buff_tool.buffer_corner_cb.currentIndex() + 1
self.draw_app.buffer_ext(buffer_distance, join_style)
ret_val = self.draw_app.buffer_ext(buffer_distance, join_style)
self.app.ui.notebook.setTabText(2, _("Tools"))
self.draw_app.app.ui.splitter.setSizes([0, 1])
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit(_("[success] Done. Buffer Ext Tool completed."))
def activate(self):
@ -2922,9 +2924,9 @@ class FCTransform(FCShapeTool):
self.draw_app.transform_tool.run()
# ##################### ##
# # ## Main Application # ##
# ##################### ##
# ###############################################
# ################ Main Application #############
# ###############################################
class FlatCAMGeoEditor(QtCore.QObject):
transform_complete = QtCore.pyqtSignal()
@ -4230,11 +4232,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
# deselect everything
self.selected = []
self.replot()
return
return 'fail'
if len(selected) == 0:
self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for buffering."))
return
return 'fail'
if not isinstance(buf_distance, float):
self.app.inform.emit(_("[WARNING_NOTCL] Invalid distance for buffering."))
@ -4242,17 +4244,32 @@ class FlatCAMGeoEditor(QtCore.QObject):
# deselect everything
self.selected = []
self.replot()
return
return 'fail'
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(buf_distance - 1e-10, resolution=32, join_style=join_style)
if results.is_empty:
results = []
for t in selected:
if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo.exterior).buffer(
buf_distance - 1e-10,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style)
)
else:
results.append(t.geo.buffer(
buf_distance - 1e-10,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style)
)
if not results:
self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a different buffer value."))
# deselect everything
self.selected = []
self.replot()
return
self.add_shape(DrawToolShape(results))
return 'fail'
for sha in results:
self.add_shape(DrawToolShape(sha))
self.replot()
self.app.inform.emit(_("[success] Full buffer geometry created."))
@ -4262,77 +4279,48 @@ class FlatCAMGeoEditor(QtCore.QObject):
if buf_distance < 0:
self.app.inform.emit(
_("[ERROR_NOTCL] Negative buffer value is not accepted. "
"Use Buffer interior to generate an 'inside' shape")
_("[ERROR_NOTCL] Negative buffer value is not accepted.")
)
# deselect everything
self.selected = []
self.replot()
return
return 'fail'
if len(selected) == 0:
self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for buffering."))
return
return 'fail'
if not isinstance(buf_distance, float):
self.app.inform.emit(_("[WARNING_NOTCL] Invalid distance for buffering."))
# deselect everything
self.selected = []
self.replot()
return
return 'fail'
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(buf_distance + 1e-10, resolution=32, join_style=join_style)
results = []
for t in selected:
if isinstance(t.geo, LinearRing):
t.geo = Polygon(t.geo)
if results.is_empty:
if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo).buffer(
-buf_distance + 1e-10,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style)
)
if not results:
self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a smaller buffer value."))
# deselect everything
self.selected = []
self.replot()
return
return 'fail'
if type(results) == MultiPolygon:
for poly in results:
for interior in poly.interiors:
self.add_shape(DrawToolShape(interior))
else:
for interior in results:
self.add_shape(DrawToolShape(interior))
for sha in results:
self.add_shape(DrawToolShape(sha))
self.replot()
self.app.inform.emit(_("[success] Interior buffer geometry created."))
# selected = self.get_selected()
#
# if len(selected) == 0:
# self.app.inform.emit("[WARNING] Nothing selected for buffering.")
# return
#
# if not isinstance(buf_distance, float):
# self.app.inform.emit("[WARNING] Invalid distance for buffering.")
# return
#
# pre_buffer = cascaded_union([t.geo for t in selected])
# results = pre_buffer.buffer(buf_distance)
# if results.is_empty:
# self.app.inform.emit("Failed. Choose a smaller buffer value.")
# return
#
# int_geo = []
# if type(results) == MultiPolygon:
# for poly in results:
# for g in poly.interiors:
# int_geo.append(g)
# res = cascaded_union(int_geo)
# self.add_shape(DrawToolShape(res))
# else:
# print(results.interiors)
# for g in results.interiors:
# int_geo.append(g)
# res = cascaded_union(int_geo)
# self.add_shape(DrawToolShape(res))
#
# self.replot()
# self.app.inform.emit("Interior buffer geometry created.")
def buffer_ext(self, buf_distance, join_style):
selected = self.get_selected()
@ -4356,19 +4344,27 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.replot()
return
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(buf_distance - 1e-10, resolution=32, join_style=join_style)
if results.is_empty:
results = []
for t in selected:
if isinstance(t.geo, LinearRing):
t.geo = Polygon(t.geo)
if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo).buffer(
buf_distance,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style)
)
if not results:
self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a different buffer value."))
# deselect everything
self.selected = []
self.replot()
return
if type(results) == MultiPolygon:
for poly in results:
self.add_shape(DrawToolShape(poly.exterior))
else:
self.add_shape(DrawToolShape(results.exterior))
for sha in results:
self.add_shape(DrawToolShape(sha))
self.replot()
self.app.inform.emit(_("[success] Exterior buffer geometry created."))

View File

@ -2195,6 +2195,9 @@ class FCApertureSelect(DrawTool):
# bending modes using in FCRegion and FCTrack
self.draw_app.bend_mode = 1
# here store the selected apertures
self.sel_aperture = set()
try:
self.grb_editor_app.apertures_table.clearSelection()
except Exception as e:
@ -2202,6 +2205,7 @@ class FCApertureSelect(DrawTool):
self.grb_editor_app.hide_tool('all')
self.grb_editor_app.hide_tool('select')
self.grb_editor_app.array_frame.hide()
try:
QtGui.QGuiApplication.restoreOverrideCursor()
@ -2213,42 +2217,47 @@ class FCApertureSelect(DrawTool):
def click(self, point):
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control':
if key_modifier == Qt.ControlModifier:
pass
else:
self.grb_editor_app.selected = []
if key_modifier == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif key_modifier == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
if key_modifier == Qt.ShiftModifier:
pass
else:
self.grb_editor_app.selected = []
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
pass
else:
self.grb_editor_app.selected = []
def click_release(self, point):
self.grb_editor_app.apertures_table.clearSelection()
sel_aperture = set()
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if key_modifier == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif key_modifier == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
mod_key = None
for storage in self.grb_editor_app.storage_dict:
try:
for geo_el in self.grb_editor_app.storage_dict[storage]['geometry']:
if 'solid' in geo_el.geo:
geometric_data = geo_el.geo['solid']
if Point(point).within(geometric_data):
if (self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control' and
key_modifier == Qt.ControlModifier) or \
(self.grb_editor_app.app.defaults["global_mselect_key"] == 'Shift' and
key_modifier == Qt.ShiftModifier):
if mod_key == self.grb_editor_app.app.defaults["global_mselect_key"]:
if geo_el in self.draw_app.selected:
self.draw_app.selected.remove(geo_el)
self.sel_aperture.remove(storage)
else:
# add the object to the selected shapes
self.draw_app.selected.append(geo_el)
sel_aperture.add(storage)
self.sel_aperture.add(storage)
else:
self.draw_app.selected.append(geo_el)
sel_aperture.add(storage)
self.sel_aperture.add(storage)
except KeyError:
pass
@ -2259,7 +2268,7 @@ class FCApertureSelect(DrawTool):
log.debug("FlatCAMGrbEditor.FCApertureSelect.click_release() --> %s" % str(e))
self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
for aper in sel_aperture:
for aper in self.sel_aperture:
for row in range(self.grb_editor_app.apertures_table.rowCount()):
if str(aper) == self.grb_editor_app.apertures_table.item(row, 1).text():
self.grb_editor_app.apertures_table.selectRow(row)
@ -2345,7 +2354,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
# #########################
# ### Gerber Apertures ####
# #########################
self.apertures_table_label = QtWidgets.QLabel(_('<b>Apertures:</b>'))
self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
self.apertures_table_label.setToolTip(
_("Apertures Table for the Gerber Object.")
)
@ -2391,7 +2400,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
grid1 = QtWidgets.QGridLayout()
self.apertures_box.addLayout(grid1)
apcode_lbl = QtWidgets.QLabel(_('Aperture Code:'))
apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
apcode_lbl.setToolTip(
_("Code for the new aperture")
)
@ -2401,7 +2410,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apcode_entry.setValidator(QtGui.QIntValidator(0, 999))
grid1.addWidget(self.apcode_entry, 1, 1)
apsize_lbl = QtWidgets.QLabel(_('Aperture Size:'))
apsize_lbl = QtWidgets.QLabel('%s:' % _('Aperture Size'))
apsize_lbl.setToolTip(
_("Size for the new aperture.\n"
"If aperture type is 'R' or 'O' then\n"
@ -2415,7 +2424,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apsize_entry.setValidator(QtGui.QDoubleValidator(0.0001, 99.9999, 4))
grid1.addWidget(self.apsize_entry, 2, 1)
aptype_lbl = QtWidgets.QLabel(_('Aperture Type:'))
aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
aptype_lbl.setToolTip(
_("Select the type of new aperture. Can be:\n"
"C = circular\n"
@ -2428,7 +2437,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.aptype_cb.addItems(['C', 'R', 'O'])
grid1.addWidget(self.aptype_cb, 3, 1)
self.apdim_lbl = QtWidgets.QLabel(_('Aperture Dim:'))
self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
self.apdim_lbl.setToolTip(
_("Dimensions for the new aperture.\n"
"Active only for rectangular apertures (type R).\n"
@ -2484,8 +2493,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
# Buffer distance
self.buffer_distance_entry = FCEntry()
buf_form_layout.addRow(_("Buffer distance:"), self.buffer_distance_entry)
self.buffer_corner_lbl = QtWidgets.QLabel(_("Buffer corner:"))
buf_form_layout.addRow('%s:' % _("Buffer distance"), self.buffer_distance_entry)
self.buffer_corner_lbl = QtWidgets.QLabel('%s:' % _("Buffer corner"))
self.buffer_corner_lbl.setToolTip(
_("There are 3 types of corners:\n"
" - 'Round': the corner is rounded.\n"
@ -2517,7 +2526,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.scale_tool_frame.hide()
# Title
scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Scale Aperture:'))
scale_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Aperture'))
scale_title_lbl.setToolTip(
_("Scale a aperture in the aperture list")
)
@ -2527,7 +2536,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
scale_form_layout = QtWidgets.QFormLayout()
self.scale_tools_box.addLayout(scale_form_layout)
self.scale_factor_lbl = QtWidgets.QLabel(_("Scale factor:"))
self.scale_factor_lbl = QtWidgets.QLabel('%s:' % _("Scale factor"))
self.scale_factor_lbl.setToolTip(
_("The factor by which to scale the selected aperture.\n"
"Values can be between 0.0000 and 999.9999")
@ -2555,7 +2564,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.ma_tool_frame.hide()
# Title
ma_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Mark polygon areas:'))
ma_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Mark polygon areas'))
ma_title_lbl.setToolTip(
_("Mark the polygon areas.")
)
@ -2565,7 +2574,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
ma_form_layout = QtWidgets.QFormLayout()
self.ma_tools_box.addLayout(ma_form_layout)
self.ma_upper_threshold_lbl = QtWidgets.QLabel(_("Area UPPER threshold:"))
self.ma_upper_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area UPPER threshold"))
self.ma_upper_threshold_lbl.setToolTip(
_("The threshold value, all areas less than this are marked.\n"
"Can have a value between 0.0000 and 9999.9999")
@ -2573,7 +2582,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.ma_upper_threshold_entry = FCEntry()
self.ma_upper_threshold_entry.setValidator(QtGui.QDoubleValidator(0.0000, 9999.9999, 4))
self.ma_lower_threshold_lbl = QtWidgets.QLabel(_("Area LOWER threshold:"))
self.ma_lower_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area LOWER threshold"))
self.ma_lower_threshold_lbl.setToolTip(
_("The threshold value, all areas more than this are marked.\n"
"Can have a value between 0.0000 and 9999.9999")
@ -2594,7 +2603,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
# ######################
# ### Add Pad Array ####
# ######################
# add a frame and inside add a vertical box layout. Inside this vbox layout I add
# all the add Pad array widgets
# this way I can hide/show the frame
@ -2627,7 +2635,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.array_form = QtWidgets.QFormLayout()
self.array_box.addLayout(self.array_form)
self.pad_array_size_label = QtWidgets.QLabel(_('Nr of pads:'))
self.pad_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
self.pad_array_size_label.setToolTip(
_("Specify how many pads to be in the array.")
)
@ -2646,7 +2654,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.linear_form = QtWidgets.QFormLayout()
self.linear_box.addLayout(self.linear_form)
self.pad_axis_label = QtWidgets.QLabel(_('Direction:'))
self.pad_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
self.pad_axis_label.setToolTip(
_("Direction on which the linear array is oriented:\n"
"- 'X' - horizontal axis \n"
@ -2661,7 +2669,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_axis_radio.set_value('X')
self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
self.pad_pitch_label = QtWidgets.QLabel(_('Pitch:'))
self.pad_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
self.pad_pitch_label.setToolTip(
_("Pitch = Distance between elements of the array.")
)
@ -2670,7 +2678,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_pitch_entry = LengthEntry()
self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
self.linear_angle_label = QtWidgets.QLabel(_('Angle:'))
self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
self.linear_angle_label.setToolTip(
_( "Angle at which the linear array is placed.\n"
"The precision is of max 2 decimals.\n"
@ -2691,7 +2699,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.circular_box.setContentsMargins(0, 0, 0, 0)
self.array_circular_frame.setLayout(self.circular_box)
self.pad_direction_label = QtWidgets.QLabel(_('Direction:'))
self.pad_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
self.pad_direction_label.setToolTip(
_("Direction for circular array."
"Can be CW = clockwise or CCW = counter clockwise.")
@ -2706,7 +2714,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_direction_radio.set_value('CW')
self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
self.pad_angle_label = QtWidgets.QLabel(_('Angle:'))
self.pad_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
self.pad_angle_label.setToolTip(
_("Angle at which each element in circular array is placed.")
)
@ -2944,23 +2952,24 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.tool2tooldia[i + 1] = tt_aperture
# Init GUI
if self.units == 'MM':
self.buffer_distance_entry.set_value(0.01)
self.scale_factor_entry.set_value(1.0)
self.ma_upper_threshold_entry.set_value(1.0)
self.apsize_entry.set_value(1.00)
else:
self.buffer_distance_entry.set_value(0.0003937)
self.scale_factor_entry.set_value(0.03937)
self.ma_upper_threshold_entry.set_value(0.00155)
self.apsize_entry.set_value(0.039)
self.ma_lower_threshold_entry.set_value(0.0)
self.pad_array_size_entry.set_value(5)
self.pad_pitch_entry.set_value(2.54)
self.pad_angle_entry.set_value(12)
self.pad_direction_radio.set_value('CW')
self.pad_axis_radio.set_value('X')
self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
self.ma_upper_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_low"])
self.ma_lower_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_high"])
self.apsize_entry.set_value(self.app.defaults["gerber_editor_newsize"])
self.aptype_cb.set_value(self.app.defaults["gerber_editor_newtype"])
self.apdim_entry.set_value(self.app.defaults["gerber_editor_newdim"])
self.pad_array_size_entry.set_value(self.app.defaults["gerber_editor_array_size"])
# linear array
self.pad_axis_radio.set_value(self.app.defaults["gerber_editor_lin_axis"])
self.pad_pitch_entry.set_value(self.app.defaults["gerber_editor_lin_pitch"])
self.linear_angle_spinner.set_value(self.app.defaults["gerber_editor_lin_angle"])
# circular array
self.pad_direction_radio.set_value(self.app.defaults["gerber_editor_circ_dir"])
self.pad_angle_entry.set_value(self.app.defaults["gerber_editor_circ_angle"])
def build_ui(self, first_run=None):
@ -3113,7 +3122,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apcode_entry.set_value(max(self.tool2tooldia.values()) + 1)
except ValueError:
# this means that the edited object has no apertures so we start with 10 (Gerber specifications)
self.apcode_entry.set_value(10)
self.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
def on_aperture_add(self, apid=None):
self.is_modified = True

File diff suppressed because it is too large Load Diff

View File

@ -171,9 +171,11 @@ class LengthEntry(QtWidgets.QLineEdit):
self.readyToEdit = False
def focusOutEvent(self, e):
super(LengthEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(LengthEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
@ -225,9 +227,11 @@ class FloatEntry(QtWidgets.QLineEdit):
self.readyToEdit = False
def focusOutEvent(self, e):
super(FloatEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(FloatEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
@ -274,9 +278,11 @@ class FloatEntry2(QtWidgets.QLineEdit):
self.readyToEdit = False
def focusOutEvent(self, e):
super(FloatEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(FloatEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def get_value(self):
raw = str(self.text()).strip(' ')
@ -316,9 +322,11 @@ class IntEntry(QtWidgets.QLineEdit):
self.readyToEdit = False
def focusOutEvent(self, e):
super(IntEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(IntEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def get_value(self):
@ -353,16 +361,17 @@ class FCEntry(QtWidgets.QLineEdit):
def on_edit_finished(self):
self.clearFocus()
def mousePressEvent(self, e, Parent=None):
def mousePressEvent(self, e, parent=None):
super(FCEntry, self).mousePressEvent(e) # required to deselect on 2e click
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
super(FCEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
if e.reason() != QtCore.Qt.PopupFocusReason:
super(FCEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def get_value(self):
return str(self.text())
@ -381,36 +390,24 @@ class FCEntry(QtWidgets.QLineEdit):
class FCEntry2(FCEntry):
def __init__(self, parent=None):
super(FCEntry2, self).__init__(parent)
self.readyToEdit = True
self.editingFinished.connect(self.on_edit_finished)
def on_edit_finished(self):
self.clearFocus()
def set_value(self, val, decimals=4):
try:
fval = float(val)
except ValueError:
return
self.setText('%.*f' % (decimals, fval))
class FCEntry3(FCEntry):
def __init__(self, parent=None):
super(FCEntry3, self).__init__(parent)
self.readyToEdit = True
self.editingFinished.connect(self.on_edit_finished)
def on_edit_finished(self):
self.clearFocus()
def set_value(self, val, decimals=4):
try:
fval = float(val)
except ValueError:
return
self.setText('%.*f' % (decimals, fval))
def get_value(self):
@ -432,16 +429,17 @@ class EvalEntry(QtWidgets.QLineEdit):
def on_edit_finished(self):
self.clearFocus()
def mousePressEvent(self, e, Parent=None):
def mousePressEvent(self, e, parent=None):
super(EvalEntry, self).mousePressEvent(e) # required to deselect on 2e click
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
super(EvalEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
if e.reason() != QtCore.Qt.PopupFocusReason:
super(EvalEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
@ -478,16 +476,17 @@ class EvalEntry2(QtWidgets.QLineEdit):
def on_edit_finished(self):
self.clearFocus()
def mousePressEvent(self, e, Parent=None):
def mousePressEvent(self, e, parent=None):
super(EvalEntry2, self).mousePressEvent(e) # required to deselect on 2e click
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
super(EvalEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
if e.reason() != QtCore.Qt.PopupFocusReason:
super(EvalEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
def get_value(self):
raw = str(self.text()).strip(' ')
@ -847,9 +846,9 @@ class FCDetachableTab(QtWidgets.QTabWidget):
super().__init__()
self.tabBar = self.FCTabBar(self)
self.tabBar.onDetachTabSignal.connect(self.detachTab)
self.tabBar.onMoveTabSignal.connect(self.moveTab)
self.tabBar.detachedTabDropSignal.connect(self.detachedTabDrop)
self.set_detachable(val=True)
self.setTabBar(self.tabBar)
@ -872,6 +871,48 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.setTabsClosable(True)
self.tabCloseRequested.connect(self.closeTab)
def set_rmb_callback(self, callback):
"""
:param callback: Function to call on right mouse click on tab
:type callback: func
:return: None
"""
self.tabBar.right_click.connect(callback)
def set_detachable(self, val=True):
try:
self.tabBar.onDetachTabSignal.disconnect()
except TypeError:
pass
if val is True:
self.tabBar.onDetachTabSignal.connect(self.detachTab)
# the tab can be moved around
self.tabBar.can_be_dragged = True
else:
# the detached tab can't be moved
self.tabBar.can_be_dragged = False
return val
def setupContextMenu(self):
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
def addContextMenu(self, entry, call_function, icon=None, initial_checked=False):
action_name = str(entry)
action = QtWidgets.QAction(self)
action.setCheckable(True)
action.setText(action_name)
if icon:
assert isinstance(icon, QtGui.QIcon), \
"Expected the argument to be QtGui.QIcon. Instead it is %s" % type(icon)
action.setIcon(icon)
action.setChecked(initial_checked)
self.addAction(action)
action.triggered.connect(call_function)
def useOldIndex(self, param):
if param:
self.use_old_index = True
@ -1209,6 +1250,8 @@ class FCDetachableTab(QtWidgets.QTabWidget):
onMoveTabSignal = pyqtSignal(int, int)
detachedTabDropSignal = pyqtSignal(str, int, QtCore.QPoint)
right_click = pyqtSignal(int)
def __init__(self, parent=None):
QtWidgets.QTabBar.__init__(self, parent)
@ -1216,11 +1259,16 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.setElideMode(QtCore.Qt.ElideRight)
self.setSelectionBehaviorOnRemove(QtWidgets.QTabBar.SelectLeftTab)
self.prev_index = -1
self.dragStartPos = QtCore.QPoint()
self.dragDropedPos = QtCore.QPoint()
self.mouseCursor = QtGui.QCursor()
self.dragInitiated = False
# set this to False and the tab will no longer be displayed as detached
self.can_be_dragged = True
def mouseDoubleClickEvent(self, event):
"""
Send the onDetachTabSignal when a tab is double clicked
@ -1234,21 +1282,37 @@ class FCDetachableTab(QtWidgets.QTabWidget):
def mousePressEvent(self, event):
"""
Set the starting position for a drag event when the mouse button is pressed
Set the starting position for a drag event when the left mouse button is pressed.
Start detection of a right mouse click.
:param event: a mouse press event
:return:
"""
if event.button() == QtCore.Qt.LeftButton:
self.dragStartPos = event.pos()
elif event.button() == QtCore.Qt.RightButton:
self.prev_index = self.tabAt(event.pos())
self.dragDropedPos.setX(0)
self.dragDropedPos.setY(0)
self.dragInitiated = False
QtWidgets.QTabBar.mousePressEvent(self, event)
def mouseReleaseEvent(self, event):
"""
Finish the detection of the right mouse click on the tab
:param event: a mouse press event
:return:
"""
if event.button() == QtCore.Qt.RightButton and self.prev_index == self.tabAt(event.pos()):
self.right_click.emit(self.prev_index)
self.prev_index = -1
QtWidgets.QTabBar.mouseReleaseEvent(self, event)
def mouseMoveEvent(self, event):
"""
Determine if the current movement is a drag. If it is, convert it into a QDrag. If the
@ -1264,7 +1328,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.dragInitiated = True
# If the current movement is a drag initiated by the left button
if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated):
if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated and self.can_be_dragged):
# Stop the move event
finishMoveEvent = QtGui.QMouseEvent(
@ -1561,9 +1625,11 @@ class FCSpinner(QtWidgets.QSpinBox):
self.readyToEdit = False
def focusOutEvent(self, e):
super(FCSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(FCSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
def get_value(self):
return str(self.value())
@ -1600,9 +1666,11 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
self.readyToEdit = False
def focusOutEvent(self, e):
super(FCDoubleSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(FCDoubleSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
def get_value(self):
return str(self.value())
@ -1647,9 +1715,11 @@ class Dialog_box(QtWidgets.QWidget):
self.readyToEdit = False
def focusOutEvent(self, e):
super(Dialog_box, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
# don't focus out if the user requests an popup menu
if e.reason() != QtCore.Qt.PopupFocusReason:
super(Dialog_box, self).focusOutEvent(e) # required to remove cursor on focusOut
self.lineEdit().deselect()
self.readyToEdit = True
class _BrowserTextEdit(QTextEdit):
@ -1811,9 +1881,16 @@ class MyCompleter(QCompleter):
QCompleter.__init__(self)
self.setCompletionMode(QCompleter.PopupCompletion)
self.highlighted.connect(self.setHighlighted)
# self.popup().installEventFilter(self)
# def eventFilter(self, obj, event):
# if event.type() == QtCore.QEvent.Wheel and obj is self.popup():
# pass
# return False
def setHighlighted(self, text):
self.lastSelected = text
def getSelected(self):
return self.lastSelected

View File

@ -76,7 +76,7 @@ class ObjectUI(QtWidgets.QWidget):
# ###########################
# ### Scale ####
self.scale_label = QtWidgets.QLabel(_('<b>Scale:</b>'))
self.scale_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scale'))
self.scale_label.setToolTip(
_("Change the size of the object.")
)
@ -86,7 +86,7 @@ class ObjectUI(QtWidgets.QWidget):
layout.addLayout(self.scale_grid)
# Factor
faclabel = QtWidgets.QLabel(_('Factor:'))
faclabel = QtWidgets.QLabel('%s:' % _('Factor'))
faclabel.setToolTip(
_("Factor by which to multiply\n"
"geometric features of this object.")
@ -105,7 +105,7 @@ class ObjectUI(QtWidgets.QWidget):
self.scale_grid.addWidget(self.scale_button, 0, 2)
# ### Offset ####
self.offset_label = QtWidgets.QLabel(_('<b>Offset:</b>'))
self.offset_label = QtWidgets.QLabel('<b>%s:</b>' % _('Offset'))
self.offset_label.setToolTip(
_("Change the position of this object.")
)
@ -114,7 +114,7 @@ class ObjectUI(QtWidgets.QWidget):
self.offset_grid = QtWidgets.QGridLayout()
layout.addLayout(self.offset_grid)
self.offset_vectorlabel = QtWidgets.QLabel(_('Vector:'))
self.offset_vectorlabel = QtWidgets.QLabel('%s:' % _('Vector'))
self.offset_vectorlabel.setToolTip(
_("Amount by which to move the object\n"
"in the x and y axes in (x, y) format.")
@ -147,7 +147,7 @@ class GerberObjectUI(ObjectUI):
grid0.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.custom_box.addLayout(grid0)
self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.plot_options_label.setMinimumWidth(90)
grid0.addWidget(self.plot_options_label, 0, 0)
@ -179,7 +179,7 @@ class GerberObjectUI(ObjectUI):
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
@ -189,7 +189,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(hlay_plot)
# ### Gerber Apertures ####
self.apertures_table_label = QtWidgets.QLabel(_('<b>Apertures:</b>'))
self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
self.apertures_table_label.setToolTip(
_("Apertures Table for the Gerber Object.")
)
@ -247,7 +247,7 @@ class GerberObjectUI(ObjectUI):
self.apertures_table.setVisible(False)
# Isolation Routing
self.isolation_routing_label = QtWidgets.QLabel(_("<b>Isolation Routing:</b>"))
self.isolation_routing_label = QtWidgets.QLabel("<b>%s:</b>" % _("Isolation Routing"))
self.isolation_routing_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut outside polygons.")
@ -256,7 +256,7 @@ class GerberObjectUI(ObjectUI):
grid1 = QtWidgets.QGridLayout()
self.custom_box.addLayout(grid1)
tdlabel = QtWidgets.QLabel(_('Tool dia:'))
tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
tdlabel.setToolTip(
_("Diameter of the cutting tool.\n"
"If you want to have an isolation path\n"
@ -269,7 +269,7 @@ class GerberObjectUI(ObjectUI):
self.iso_tool_dia_entry = LengthEntry()
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
passlabel = QtWidgets.QLabel(_('Passes:'))
passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
passlabel.setToolTip(
_("Width of the isolation gap in\n"
"number (integer) of tool widths.")
@ -280,7 +280,7 @@ class GerberObjectUI(ObjectUI):
self.iso_width_entry.setRange(1, 999)
grid1.addWidget(self.iso_width_entry, 1, 1)
overlabel = QtWidgets.QLabel(_('Pass overlap:'))
overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
overlabel.setToolTip(
_("How much (fraction) of the tool width to overlap each tool pass.\n"
"Example:\n"
@ -292,7 +292,7 @@ class GerberObjectUI(ObjectUI):
grid1.addWidget(self.iso_overlap_entry, 2, 1)
# Milling Type Radio Button
self.milling_type_label = QtWidgets.QLabel(_('Milling Type:'))
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
self.milling_type_label.setToolTip(
_("Milling type:\n"
"- climb / best for precision milling and to reduce tool usage\n"
@ -304,7 +304,7 @@ class GerberObjectUI(ObjectUI):
grid1.addWidget(self.milling_type_radio, 3, 1)
# combine all passes CB
self.combine_passes_cb = FCCheckBox(label=_('Combine'))
self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
self.combine_passes_cb.setToolTip(
_("Combine all passes into one object")
)
@ -320,7 +320,7 @@ class GerberObjectUI(ObjectUI):
)
grid1.addWidget(self.follow_cb, 4, 1)
self.gen_iso_label = QtWidgets.QLabel(_("<b>Generate Isolation Geometry:</b>"))
self.gen_iso_label = QtWidgets.QLabel("<b>%s:</b>" % _("Generate Isolation Geometry"))
self.gen_iso_label.setToolTip(
_("Create a Geometry object with toolpaths to cut \n"
"isolation outside, inside or on both sides of the\n"
@ -379,7 +379,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(grid2)
# ## Clear non-copper regions
self.clearcopper_label = QtWidgets.QLabel(_("<b>Clear N-copper:</b>"))
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Clear N-copper"))
self.clearcopper_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut all non-copper regions.")
@ -395,7 +395,7 @@ class GerberObjectUI(ObjectUI):
grid2.addWidget(self.generate_ncc_button, 0, 1)
# ## Board cutout
self.board_cutout_label = QtWidgets.QLabel(_("<b>Board cutout:</b>"))
self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Board cutout"))
self.board_cutout_label.setToolTip(
_("Create toolpaths to cut around\n"
"the PCB and separate it from\n"
@ -411,7 +411,7 @@ class GerberObjectUI(ObjectUI):
grid2.addWidget(self.generate_cutout_button, 1, 1)
# ## Non-copper regions
self.noncopper_label = QtWidgets.QLabel(_("<b>Non-copper regions:</b>"))
self.noncopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
self.noncopper_label.setToolTip(
_("Create polygons covering the\n"
"areas without copper on the PCB.\n"
@ -425,7 +425,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(grid4)
# Margin
bmlabel = QtWidgets.QLabel(_('Boundary Margin:'))
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bmlabel.setToolTip(
_("Specify the edge of the PCB\n"
"by drawing a box around all\n"
@ -449,7 +449,7 @@ class GerberObjectUI(ObjectUI):
grid4.addWidget(self.generate_noncopper_button, 1, 1)
# ## Bounding box
self.boundingbox_label = QtWidgets.QLabel(_('<b>Bounding Box:</b>'))
self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
self.boundingbox_label.setToolTip(
_("Create a geometry surrounding the Gerber object.\n"
"Square shape.")
@ -459,7 +459,7 @@ class GerberObjectUI(ObjectUI):
grid5 = QtWidgets.QGridLayout()
self.custom_box.addLayout(grid5)
bbmargin = QtWidgets.QLabel(_('Boundary Margin:'))
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bbmargin.setToolTip(
_("Distance of the edges of the box\n"
"to the nearest polygon.")
@ -500,7 +500,7 @@ class ExcellonObjectUI(ObjectUI):
hlay_plot = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay_plot)
self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.solid_cb = FCCheckBox(label=_('Solid'))
self.solid_cb.setToolTip(
_("Solid circles.")
@ -512,7 +512,7 @@ class ExcellonObjectUI(ObjectUI):
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
@ -531,7 +531,7 @@ class ExcellonObjectUI(ObjectUI):
self.tools_box.addLayout(hlay_plot)
# ### Tools Drills ####
self.tools_table_label = QtWidgets.QLabel(_('<b>Tools Table</b>'))
self.tools_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tools Table'))
self.tools_table_label.setToolTip(
_("Tools in this Excellon object\n"
"when are used for drilling.")
@ -539,7 +539,7 @@ class ExcellonObjectUI(ObjectUI):
hlay_plot.addWidget(self.tools_table_label)
# Plot CB
self.plot_cb = FCCheckBox(_('Plot Object'))
self.plot_cb = FCCheckBox(_('Plot'))
self.plot_cb.setToolTip(
_("Plot (show) this object.")
)
@ -579,7 +579,7 @@ class ExcellonObjectUI(ObjectUI):
self.tools_box.addWidget(self.empty_label)
# ### Create CNC Job ####
self.cncjob_label = QtWidgets.QLabel(_('<b>Create CNC Job</b>'))
self.cncjob_label = QtWidgets.QLabel('<b>%s</b>' % _('Create CNC Job'))
self.cncjob_label.setToolTip(
_("Create a CNC Job object\n"
"for this drill object.")
@ -590,7 +590,7 @@ class ExcellonObjectUI(ObjectUI):
self.tools_box.addLayout(grid1)
# Cut Z
cutzlabel = QtWidgets.QLabel(_('Cut Z:'))
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
cutzlabel.setToolTip(
_("Drill depth (negative)\n"
"below the copper surface.")
@ -600,7 +600,7 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.cutz_entry, 0, 1)
# Travel Z (z_move)
travelzlabel = QtWidgets.QLabel(_('Travel Z:'))
travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
travelzlabel.setToolTip(
_("Tool height when travelling\n"
"across the XY plane.")
@ -610,7 +610,7 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.travelz_entry, 1, 1)
# Tool change:
self.toolchange_cb = FCCheckBox(_("Tool change"))
self.toolchange_cb = FCCheckBox('%s:' % _("Tool change"))
self.toolchange_cb.setToolTip(
_("Include tool-change sequence\n"
"in G-Code (Pause for tool change).")
@ -618,7 +618,7 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.toolchange_cb, 2, 0)
# Tool change Z:
toolchzlabel = QtWidgets.QLabel(_("Tool change Z:"))
toolchzlabel = QtWidgets.QLabel('%s:' % _("Tool change Z"))
toolchzlabel.setToolTip(
_("Z-axis position (height) for\n"
"tool change.")
@ -629,9 +629,9 @@ class ExcellonObjectUI(ObjectUI):
self.ois_tcz_e = OptionalInputSection(self.toolchange_cb, [self.toolchangez_entry])
# Start move Z:
self.estartz_label = QtWidgets.QLabel(_("Start move Z:"))
self.estartz_label = QtWidgets.QLabel('%s:' % _("Start move Z"))
self.estartz_label.setToolTip(
_("Tool height just before starting the work.\n"
_("Height of the tool just after start.\n"
"Delete the value if you don't need this feature.")
)
grid1.addWidget(self.estartz_label, 4, 0)
@ -639,17 +639,17 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.estartz_entry, 4, 1)
# End move Z:
self.eendz_label = QtWidgets.QLabel(_("End move Z:"))
self.eendz_label = QtWidgets.QLabel('%s:' % _("End move Z"))
self.eendz_label.setToolTip(
_("Z-axis position (height) for\n"
"the last move.")
_("Height of the tool after\n"
"the last move at the end of the job.")
)
grid1.addWidget(self.eendz_label, 5, 0)
self.eendz_entry = LengthEntry()
grid1.addWidget(self.eendz_entry, 5, 1)
# Excellon Feedrate
frlabel = QtWidgets.QLabel(_('Feedrate (Plunge):'))
frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge):'))
frlabel.setToolTip(
_("Tool speed while drilling\n"
"(in units per minute).\n"
@ -660,14 +660,13 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.feedrate_entry, 6, 1)
# Excellon Rapid Feedrate
self.feedrate_rapid_label = QtWidgets.QLabel(_('Feedrate Rapids:'))
self.feedrate_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
self.feedrate_rapid_label.setToolTip(
_("Tool speed while drilling\n"
"(in units per minute).\n"
"This is for the rapid move G00.\n"
"It is useful only for Marlin,\n"
"ignore for any other cases."
)
"ignore for any other cases.")
)
grid1.addWidget(self.feedrate_rapid_label, 7, 0)
self.feedrate_rapid_entry = LengthEntry()
@ -677,7 +676,7 @@ class ExcellonObjectUI(ObjectUI):
self.feedrate_rapid_entry.hide()
# Spindlespeed
spdlabel = QtWidgets.QLabel(_('Spindle speed:'))
spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
spdlabel.setToolTip(
_("Speed of the spindle\n"
"in RPM (optional)")
@ -687,14 +686,14 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.spindlespeed_entry, 8, 1)
# Dwell
self.dwell_cb = FCCheckBox(_('Dwell:'))
self.dwell_cb = FCCheckBox('%s:' % _('Dwell'))
self.dwell_cb.setToolTip(
_("Pause to allow the spindle to reach its\n"
"speed before cutting.")
)
self.dwelltime_entry = FCEntry()
self.dwelltime_entry.setToolTip(
_("Number of milliseconds for spindle to dwell.")
_("Number of time units for spindle to dwell.")
)
grid1.addWidget(self.dwell_cb, 9, 0)
grid1.addWidget(self.dwelltime_entry, 9, 1)
@ -702,10 +701,10 @@ class ExcellonObjectUI(ObjectUI):
self.ois_dwell = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
# postprocessor selection
pp_excellon_label = QtWidgets.QLabel(_("Postprocessor:"))
pp_excellon_label = QtWidgets.QLabel('%s:' % _("Postprocessor"))
pp_excellon_label.setToolTip(
_("The json file that dictates\n"
"gcode output.")
_("The postprocessor JSON file that dictates\n"
"Gcode output.")
)
self.pp_excellon_name_cb = FCComboBox()
self.pp_excellon_name_cb.setFocusPolicy(QtCore.Qt.StrongFocus)
@ -713,7 +712,7 @@ class ExcellonObjectUI(ObjectUI):
grid1.addWidget(self.pp_excellon_name_cb, 10, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel(_("Probe Z depth:"))
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
self.pdepth_label.setToolTip(
_("The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units.")
@ -725,7 +724,7 @@ class ExcellonObjectUI(ObjectUI):
self.pdepth_entry.setVisible(False)
# Probe feedrate
self.feedrate_probe_label = QtWidgets.QLabel(_("Feedrate Probe:"))
self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
self.feedrate_probe_label.setToolTip(
_("The feedrate used while the probe is probing.")
)
@ -743,7 +742,7 @@ class ExcellonObjectUI(ObjectUI):
# ### Choose what to use for Gcode creation: Drills, Slots or Both
gcode_box = QtWidgets.QFormLayout()
gcode_type_label = QtWidgets.QLabel(_('<b>Type: </b>'))
gcode_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Gcode'))
gcode_type_label.setToolTip(
_("Choose what to use for GCode generation:\n"
"'Drills', 'Slots' or 'Both'.\n"
@ -767,7 +766,7 @@ class ExcellonObjectUI(ObjectUI):
self.tools_box.addWidget(self.generate_cnc_button)
# ### Milling Holes Drills ####
self.mill_hole_label = QtWidgets.QLabel(_('<b>Mill Holes</b>'))
self.mill_hole_label = QtWidgets.QLabel('<b>%s</b>' % _('Mill Holes'))
self.mill_hole_label.setToolTip(
_("Create Geometry for milling holes.")
)
@ -781,7 +780,7 @@ class ExcellonObjectUI(ObjectUI):
grid2 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid2)
self.tdlabel = QtWidgets.QLabel(_('Drills Tool dia:'))
self.tdlabel = QtWidgets.QLabel('%s:' % _('Drill Tool dia'))
self.tdlabel.setToolTip(
_("Diameter of the cutting tool.")
)
@ -797,9 +796,10 @@ class ExcellonObjectUI(ObjectUI):
grid3 = QtWidgets.QGridLayout()
self.custom_box.addLayout(grid3)
self.stdlabel = QtWidgets.QLabel(_('Slots Tool dia:'))
self.stdlabel = QtWidgets.QLabel('%s:' % _('Slot Tool dia'))
self.stdlabel.setToolTip(
_("Diameter of the cutting tool.")
_("Diameter of the cutting tool\n"
"when milling slots.")
)
grid3.addWidget(self.stdlabel, 0, 0)
self.slot_tooldia_entry = LengthEntry()
@ -828,13 +828,13 @@ class GeometryObjectUI(ObjectUI):
icon_file='share/geometry32.png', parent=parent)
# Plot options
self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.custom_box.addWidget(self.plot_options_label)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
@ -853,7 +853,7 @@ class GeometryObjectUI(ObjectUI):
self.geo_tools_box.addLayout(hlay_plot)
# ### Tools ####
self.tools_table_label = QtWidgets.QLabel(_('<b>Tools Table</b>'))
self.tools_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tools Table'))
self.tools_table_label.setToolTip(
_("Tools in this Geometry object used for cutting.\n"
"The 'Offset' entry will set an offset for the cut.\n"
@ -945,7 +945,7 @@ class GeometryObjectUI(ObjectUI):
self.grid1 = QtWidgets.QGridLayout()
self.geo_tools_box.addLayout(self.grid1)
self.tool_offset_lbl = QtWidgets.QLabel(_('Tool Offset:'))
self.tool_offset_lbl = QtWidgets.QLabel('%s:' % _('Tool Offset'))
self.tool_offset_lbl.setToolTip(
_(
"The value to offset the cut when \n"
@ -971,7 +971,7 @@ class GeometryObjectUI(ObjectUI):
# self.addtool_label.setToolTip(
# "Add/Copy/Delete a tool to the tool list."
# )
self.addtool_entry_lbl = QtWidgets.QLabel(_('<b>Tool Dia:</b>'))
self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Tool Dia'))
self.addtool_entry_lbl.setToolTip(
_(
"Diameter for the new tool"
@ -1022,7 +1022,7 @@ class GeometryObjectUI(ObjectUI):
# Create CNC Job ###
# ##################
# ### Tools Data ## ##
self.tool_data_label = QtWidgets.QLabel(_('<b>Tool Data</b>'))
self.tool_data_label = QtWidgets.QLabel('<b>%s</b>' % _('Tool Data'))
self.tool_data_label.setToolTip(
_(
"The data used for creating GCode.\n"
@ -1043,7 +1043,7 @@ class GeometryObjectUI(ObjectUI):
self.geo_param_box.addLayout(self.grid3)
# Tip Dia
self.tipdialabel = QtWidgets.QLabel(_('V-Tip Dia:'))
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
self.tipdialabel.setToolTip(
_(
"The tip diameter for V-Shape Tool"
@ -1054,7 +1054,7 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.tipdia_entry, 1, 1)
# Tip Angle
self.tipanglelabel = QtWidgets.QLabel(_('V-Tip Angle:'))
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
self.tipanglelabel.setToolTip(
_(
"The tip angle for V-Shape Tool.\n"
@ -1066,7 +1066,7 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.tipangle_entry, 2, 1)
# Cut Z
cutzlabel = QtWidgets.QLabel(_('Cut Z:'))
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
cutzlabel.setToolTip(
_(
"Cutting depth (negative)\n"
@ -1078,15 +1078,13 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.cutz_entry, 3, 1)
# Multi-pass
self.mpass_cb = FCCheckBox(_("Multi-Depth:"))
self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
self.mpass_cb.setToolTip(
_(
"Use multiple passes to limit\n"
"the cut depth in each pass. Will\n"
"cut multiple times until Cut Z is\n"
"reached.\n"
"To the right, input the depth of \n"
"each pass (positive value)."
"reached."
)
)
self.grid3.addWidget(self.mpass_cb, 4, 0)
@ -1102,12 +1100,10 @@ class GeometryObjectUI(ObjectUI):
self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
# Travel Z
travelzlabel = QtWidgets.QLabel(_('Travel Z:'))
travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
travelzlabel.setToolTip(
_(
"Height of the tool when\n"
"moving without cutting."
)
_("Height of the tool when\n"
"moving without cutting.")
)
self.grid3.addWidget(travelzlabel, 5, 0)
self.travelz_entry = FloatEntry()
@ -1115,14 +1111,14 @@ class GeometryObjectUI(ObjectUI):
# Tool change:
self.toolchzlabel = QtWidgets.QLabel(_("Tool change Z:"))
self.toolchzlabel = QtWidgets.QLabel('%s:' %_("Tool change Z"))
self.toolchzlabel.setToolTip(
_(
"Z-axis position (height) for\n"
"tool change."
)
)
self.toolchangeg_cb = FCCheckBox(_("Tool change"))
self.toolchangeg_cb = FCCheckBox('%s:' % _("Tool change"))
self.toolchangeg_cb.setToolTip(
_(
"Include tool-change sequence\n"
@ -1148,52 +1144,44 @@ class GeometryObjectUI(ObjectUI):
# self.grid3.addWidget(self.gstartz_entry, 8, 1)
# The Z value for the end move
self.endzlabel = QtWidgets.QLabel(_('End move Z:'))
self.endzlabel = QtWidgets.QLabel('%s:' % _('End move Z'))
self.endzlabel.setToolTip(
_(
"This is the height (Z) at which the CNC\n"
"will go as the last move."
)
_("Height of the tool after\n"
"the last move at the end of the job.")
)
self.grid3.addWidget(self.endzlabel, 9, 0)
self.gendz_entry = FloatEntry()
self.grid3.addWidget(self.gendz_entry, 9, 1)
# Feedrate X-Y
frlabel = QtWidgets.QLabel(_('Feed Rate X-Y:'))
frlabel = QtWidgets.QLabel('%s:' % _('Feed Rate X-Y'))
frlabel.setToolTip(
_(
"Cutting speed in the XY\n"
"plane in units per minute"
)
_("Cutting speed in the XY\n"
"plane in units per minute")
)
self.grid3.addWidget(frlabel, 10, 0)
self.cncfeedrate_entry = FloatEntry()
self.grid3.addWidget(self.cncfeedrate_entry, 10, 1)
# Feedrate Z (Plunge)
frzlabel = QtWidgets.QLabel(_('Feed Rate Z (Plunge):'))
frzlabel = QtWidgets.QLabel('%s:' % _('Feed Rate Z'))
frzlabel.setToolTip(
_(
"Cutting speed in the Z\n"
"plane in units per minute"
)
_("Cutting speed in the XY\n"
"plane in units per minute.\n"
"It is called also Plunge.")
)
self.grid3.addWidget(frzlabel, 11, 0)
self.cncplunge_entry = FloatEntry()
self.grid3.addWidget(self.cncplunge_entry, 11, 1)
# Feedrate rapids
self.fr_rapidlabel = QtWidgets.QLabel(_('Feed Rate Rapids:'))
self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feed Rate Rapids'))
self.fr_rapidlabel.setToolTip(
_(
"Cutting speed in the XY\n"
"plane in units per minute\n"
_("Cutting speed in the XY plane\n"
"(in units per minute).\n"
"This is for the rapid move G00.\n"
"It is useful only for Marlin,\n"
"ignore for any other cases."
)
"ignore for any other cases.")
)
self.grid3.addWidget(self.fr_rapidlabel, 12, 0)
self.cncfeedrate_rapid_entry = FloatEntry()
@ -1203,19 +1191,17 @@ class GeometryObjectUI(ObjectUI):
self.cncfeedrate_rapid_entry.hide()
# Cut over 1st point in path
self.extracut_cb = FCCheckBox(_('Cut over 1st pt'))
self.extracut_cb = FCCheckBox('%s' % _('Re-cut 1st pt.'))
self.extracut_cb.setToolTip(
_(
"In order to remove possible\n"
"copper leftovers where first cut\n"
"meet with last cut, we generate an\n"
"extended cut over the first cut section."
)
_("In order to remove possible\n"
"copper leftovers where first cut\n"
"meet with last cut, we generate an\n"
"extended cut over the first cut section.")
)
self.grid3.addWidget(self.extracut_cb, 13, 0)
# Spindlespeed
spdlabel = QtWidgets.QLabel(_('Spindle speed:'))
spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
spdlabel.setToolTip(
_(
"Speed of the spindle in RPM (optional).\n"
@ -1228,7 +1214,7 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
# Dwell
self.dwell_cb = FCCheckBox(_('Dwell:'))
self.dwell_cb = FCCheckBox('%s:' % _('Dwell'))
self.dwell_cb.setToolTip(
_(
"Pause to allow the spindle to reach its\n"
@ -1237,9 +1223,7 @@ class GeometryObjectUI(ObjectUI):
)
self.dwelltime_entry = FloatEntry()
self.dwelltime_entry.setToolTip(
_(
"Number of milliseconds for spindle to dwell."
)
_("Number of time units for spindle to dwell.")
)
self.grid3.addWidget(self.dwell_cb, 15, 0)
self.grid3.addWidget(self.dwelltime_entry, 15, 1)
@ -1247,12 +1231,10 @@ class GeometryObjectUI(ObjectUI):
self.ois_dwell_geo = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
# postprocessor selection
pp_label = QtWidgets.QLabel(_("PostProcessor:"))
pp_label = QtWidgets.QLabel('%s:' % _("PostProcessor"))
pp_label.setToolTip(
_(
"The Postprocessor file that dictates\n"
"the Machine Code (like GCode, RML, HPGL) output."
)
_("The Postprocessor file that dictates\n"
"the Machine Code (like GCode, RML, HPGL) output.")
)
self.grid3.addWidget(pp_label, 16, 0)
self.pp_geometry_name_cb = FCComboBox()
@ -1260,12 +1242,10 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.pp_geometry_name_cb, 16, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel(_("Probe Z depth:"))
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
self.pdepth_label.setToolTip(
_(
"The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units."
)
_("The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units.")
)
self.grid3.addWidget(self.pdepth_label, 17, 0)
self.pdepth_entry = FCEntry()
@ -1274,11 +1254,9 @@ class GeometryObjectUI(ObjectUI):
self.pdepth_entry.setVisible(False)
# Probe feedrate
self.feedrate_probe_label = QtWidgets.QLabel(_("Feedrate Probe:"))
self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
self.feedrate_probe_label.setToolTip(
_(
"The feedrate used while the probe is probing."
)
_("The feedrate used while the probe is probing.")
)
self.grid3.addWidget(self.feedrate_probe_label, 18, 0)
self.feedrate_probe_entry = FCEntry()
@ -1297,16 +1275,14 @@ class GeometryObjectUI(ObjectUI):
# Button
self.generate_cnc_button = QtWidgets.QPushButton(_('Generate'))
self.generate_cnc_button.setToolTip(
_(
"Generate the CNC Job object."
)
_("Generate the CNC Job object.")
)
self.geo_param_box.addWidget(self.generate_cnc_button)
# ##############
# Paint area ##
# ##############
self.paint_label = QtWidgets.QLabel(_('<b>Paint Area:</b>'))
self.paint_label = QtWidgets.QLabel('<b>%s</b>' % _('Paint Area'))
self.paint_label.setToolTip(
_(
"Creates tool paths to cover the\n"
@ -1320,9 +1296,7 @@ class GeometryObjectUI(ObjectUI):
# GO Button
self.paint_tool_button = QtWidgets.QPushButton(_('Paint Tool'))
self.paint_tool_button.setToolTip(
_(
"Launch Paint Tool in Tools Tab."
)
_("Launch Paint Tool in Tools Tab.")
)
self.geo_tools_box.addWidget(self.paint_tool_button)
@ -1353,10 +1327,10 @@ class CNCObjectUI(ObjectUI):
self.offset_button.hide()
# ## Plot options
self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.custom_box.addWidget(self.plot_options_label)
self.cncplot_method_label = QtWidgets.QLabel(_("<b>Plot kind:</b>"))
self.cncplot_method_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot kind"))
self.cncplot_method_label.setToolTip(
_(
"This selects the kind of geometries on the canvas to plot.\n"
@ -1372,12 +1346,11 @@ class CNCObjectUI(ObjectUI):
{"label": _("Cut"), "value": "cut"}
], stretch=False)
self.annotation_label = QtWidgets.QLabel(_("<b>Display Annotation:</b>"))
self.annotation_label = QtWidgets.QLabel("<b>%s:</b>" % _("Display Annotation"))
self.annotation_label.setToolTip(
_(
"This selects if to display text annotation on the plot.\n"
"When checked it will display numbers in order for each end\n"
"of a travel line."
_("This selects if to display text annotation on the plot.\n"
"When checked it will display numbers in order for each end\n"
"of a travel line."
)
)
self.annotation_cb = FCCheckBox()
@ -1385,13 +1358,13 @@ class CNCObjectUI(ObjectUI):
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
self.name_hlay.addWidget(self.name_entry)
self.t_distance_label = QtWidgets.QLabel(_("<b>Travelled dist.:</b>"))
self.t_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _("Travelled dist."))
self.t_distance_label.setToolTip(
_("This is the total travelled distance on X-Y plane.\n"
"In current units.")
@ -1403,7 +1376,7 @@ class CNCObjectUI(ObjectUI):
)
self.units_label = QtWidgets.QLabel()
self.t_time_label = QtWidgets.QLabel(_("<b>Estimated time:</b>"))
self.t_time_label = QtWidgets.QLabel("<b>%s:</b>" % _("Estimated time"))
self.t_time_label.setToolTip(
_("This is the estimated time to do the routing/drilling,\n"
"without the time spent in ToolChange events.")
@ -1445,7 +1418,7 @@ class CNCObjectUI(ObjectUI):
self.custom_box.addLayout(hlay)
# CNC Tools Table for plot
self.cnc_tools_table_label = QtWidgets.QLabel(_('<b>CNC Tools Table</b>'))
self.cnc_tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('CNC Tools Table'))
self.cnc_tools_table_label.setToolTip(
_(
"Tools in this CNCJob object used for cutting.\n"
@ -1465,9 +1438,7 @@ class CNCObjectUI(ObjectUI):
# self.plot_cb = QtWidgets.QCheckBox('Plot')
self.plot_cb = FCCheckBox(_('Plot Object'))
self.plot_cb.setToolTip(
_(
"Plot (show) this object."
)
_("Plot (show) this object.")
)
self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
hlay.addStretch()
@ -1497,20 +1468,18 @@ class CNCObjectUI(ObjectUI):
# ####################
# ## Export G-Code ##
# ####################
self.export_gcode_label = QtWidgets.QLabel(_("<b>Export CNC Code:</b>"))
self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export CNC Code"))
self.export_gcode_label.setToolTip(
_("Export and save G-Code to\n"
"make this object to a file.")
"make this object to a file.")
)
self.custom_box.addWidget(self.export_gcode_label)
# Prepend text to GCode
prependlabel = QtWidgets.QLabel(_('Prepend to CNC Code:'))
prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to CNC Code'))
prependlabel.setToolTip(
_(
"Type here any G-Code commands you would\n"
"like to add to the beginning of the generated file."
)
_("Type here any G-Code commands you would\n"
"like to add at the beginning of the G-Code file.")
)
self.custom_box.addWidget(prependlabel)
@ -1518,13 +1487,11 @@ class CNCObjectUI(ObjectUI):
self.custom_box.addWidget(self.prepend_text)
# Append text to GCode
appendlabel = QtWidgets.QLabel(_('Append to CNC Code:'))
appendlabel = QtWidgets.QLabel('%s:' % _('Append to CNC Code'))
appendlabel.setToolTip(
_(
"Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)"
)
_("Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)")
)
self.custom_box.addWidget(appendlabel)
@ -1539,7 +1506,7 @@ class CNCObjectUI(ObjectUI):
self.cnc_frame.setLayout(self.cnc_box)
# Toolchange Custom G-Code
self.toolchangelabel = QtWidgets.QLabel(_('Toolchange G-Code:'))
self.toolchangelabel = QtWidgets.QLabel('%s:' % _('Toolchange G-Code'))
self.toolchangelabel.setToolTip(
_(
"Type here any G-Code commands you would\n"
@ -1561,12 +1528,10 @@ class CNCObjectUI(ObjectUI):
self.cnc_box.addLayout(cnclay)
# Toolchange Replacement Enable
self.toolchange_cb = FCCheckBox(label=_('Use Toolchange Macro'))
self.toolchange_cb = FCCheckBox(label='%s' % _('Use Toolchange Macro'))
self.toolchange_cb.setToolTip(
_(
"Check this box if you want to use\n"
"a Custom Toolchange GCode (macro)."
)
_("Check this box if you want to use\n"
"a Custom Toolchange GCode (macro).")
)
# Variable list
@ -1612,19 +1577,15 @@ class CNCObjectUI(ObjectUI):
# Edit GCode Button
self.modify_gcode_button = QtWidgets.QPushButton(_('View CNC Code'))
self.modify_gcode_button.setToolTip(
_(
"Opens TAB to view/modify/print G-Code\n"
"file."
)
_("Opens TAB to view/modify/print G-Code\n"
"file.")
)
# GO Button
self.export_gcode_button = QtWidgets.QPushButton(_('Save CNC Code'))
self.export_gcode_button.setToolTip(
_(
"Opens dialog to save G-Code\n"
"file."
)
_("Opens dialog to save G-Code\n"
"file.")
)
h_lay.addWidget(self.modify_gcode_button)

View File

@ -89,28 +89,29 @@ class ToolCalculator(FlatCAMTool):
form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout)
self.tipDia_label = QtWidgets.QLabel(_("Tip Diameter:"))
self.tipDia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
self.tipDia_entry = FCEntry()
# self.tipDia_entry.setFixedWidth(70)
self.tipDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.tipDia_label.setToolTip(_('This is the diameter of the tool tip.\n'
'The manufacturer specifies it.'))
self.tipAngle_label = QtWidgets.QLabel(_("Tip Angle:"))
self.tipDia_label.setToolTip(
_("This is the tool tip diameter.\n"
"It is specified by manufacturer.")
)
self.tipAngle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
self.tipAngle_entry = FCEntry()
# self.tipAngle_entry.setFixedWidth(70)
self.tipAngle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.tipAngle_label.setToolTip(_("This is the angle of the tip of the tool.\n"
"It is specified by manufacturer."))
self.cutDepth_label = QtWidgets.QLabel(_("Cut Z:"))
self.cutDepth_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
self.cutDepth_entry = FCEntry()
# self.cutDepth_entry.setFixedWidth(70)
self.cutDepth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.cutDepth_label.setToolTip(_("This is the depth to cut into the material.\n"
"In the CNCJob is the CutZ parameter."))
self.effectiveToolDia_label = QtWidgets.QLabel(_("Tool Diameter:"))
self.effectiveToolDia_label = QtWidgets.QLabel('%s:' % _("Tool Diameter"))
self.effectiveToolDia_entry = FCEntry()
# self.effectiveToolDia_entry.setFixedWidth(70)
self.effectiveToolDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@ -154,26 +155,26 @@ class ToolCalculator(FlatCAMTool):
plate_form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(plate_form_layout)
self.pcblengthlabel = QtWidgets.QLabel(_("Board Length:"))
self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
self.pcblength_entry = FCEntry()
# self.pcblengthlabel.setFixedWidth(70)
self.pcblength_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
self.pcbwidthlabel = QtWidgets.QLabel(_("Board Width:"))
self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
self.pcbwidth_entry = FCEntry()
# self.pcbwidthlabel.setFixedWidth(70)
self.pcbwidth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
self.cdensity_label = QtWidgets.QLabel(_("Current Density:"))
self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
self.cdensity_entry = FCEntry()
# self.cdensity_entry.setFixedWidth(70)
self.cdensity_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
"In Amps per Square Feet ASF."))
self.growth_label = QtWidgets.QLabel(_("Copper Growth:"))
self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
self.growth_entry = FCEntry()
# self.growth_entry.setFixedWidth(70)
self.growth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@ -182,7 +183,7 @@ class ToolCalculator(FlatCAMTool):
# self.growth_entry.setEnabled(False)
self.cvaluelabel = QtWidgets.QLabel(_("Current Value:"))
self.cvaluelabel = QtWidgets.QLabel('%s:' % _("Current Value"))
self.cvalue_entry = FCEntry()
# self.cvaluelabel.setFixedWidth(70)
self.cvalue_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@ -190,7 +191,7 @@ class ToolCalculator(FlatCAMTool):
'to be set on the Power Supply. In Amps.'))
self.cvalue_entry.setDisabled(True)
self.timelabel = QtWidgets.QLabel(_("Time:"))
self.timelabel = QtWidgets.QLabel('%s:' % _("Time"))
self.time_entry = FCEntry()
# self.timelabel.setFixedWidth(70)
self.time_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@ -242,7 +243,12 @@ class ToolCalculator(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -51,7 +51,7 @@ class CutOut(FlatCAMTool):
# self.type_obj_combo.setItemIcon(1, QtGui.QIcon("share/drill16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel(_("Obj Type:"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be cutout.\n"
"It can be of type: Gerber or Geometry.\n"
@ -67,14 +67,14 @@ class CutOut(FlatCAMTool):
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel(_("Object:"))
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(
_("Object to be cutout. ")
)
form_layout.addRow(self.object_label, self.obj_combo)
# Object kind
self.kindlabel = QtWidgets.QLabel(_('Obj kind:'))
self.kindlabel = QtWidgets.QLabel('%s:' % _('Obj kind'))
self.kindlabel.setToolTip(
_("Choice of what kind the object we want to cutout is.<BR>"
"- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
@ -89,7 +89,7 @@ class CutOut(FlatCAMTool):
# Tool Diameter
self.dia = FCEntry()
self.dia_label = QtWidgets.QLabel(_("Tool Dia:"))
self.dia_label = QtWidgets.QLabel('%s:' % _("Tool dia"))
self.dia_label.setToolTip(
_("Diameter of the tool used to cutout\n"
"the PCB shape out of the surrounding material.")
@ -98,7 +98,7 @@ class CutOut(FlatCAMTool):
# Margin
self.margin = FCEntry()
self.margin_label = QtWidgets.QLabel(_("Margin:"))
self.margin_label = QtWidgets.QLabel('%s:' % _("Margin:"))
self.margin_label.setToolTip(
_("Margin over bounds. A positive value here\n"
"will make the cutout of the PCB further from\n"
@ -108,7 +108,7 @@ class CutOut(FlatCAMTool):
# Gapsize
self.gapsize = FCEntry()
self.gapsize_label = QtWidgets.QLabel(_("Gap size:"))
self.gapsize_label = QtWidgets.QLabel('%s:' % _("Gap size:"))
self.gapsize_label.setToolTip(
_("The size of the bridge gaps in the cutout\n"
"used to keep the board connected to\n"
@ -127,7 +127,7 @@ class CutOut(FlatCAMTool):
# Surrounding convex box shape
self.convex_box = FCCheckBox()
self.convex_box_label = QtWidgets.QLabel(_("Convex Sh.:"))
self.convex_box_label = QtWidgets.QLabel('%s:' % _("Convex Sh."))
self.convex_box_label.setToolTip(
_("Create a convex shape surrounding the entire PCB.\n"
"Used only if the source object type is Gerber.")
@ -146,11 +146,12 @@ class CutOut(FlatCAMTool):
self.layout.addLayout(form_layout_2)
# Gaps
gaps_label = QtWidgets.QLabel(_('Gaps:'))
gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
gaps_label.setToolTip(
_("Number of gaps used for the Automatic cutout.\n"
"There can be maximum 8 bridges/gaps.\n"
"The choices are:\n"
"- None - no gaps\n"
"- lr - left + right\n"
"- tb - top + bottom\n"
"- 4 - left + right +top + bottom\n"
@ -161,7 +162,7 @@ class CutOut(FlatCAMTool):
gaps_label.setMinimumWidth(60)
self.gaps = FCComboBox()
gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
gaps_items = ['None', 'LR', 'TB', '4', '2LR', '2TB', '8']
for it in gaps_items:
self.gaps.addItem(it)
self.gaps.setStyleSheet('background-color: rgb(255,255,255)')
@ -171,7 +172,7 @@ class CutOut(FlatCAMTool):
hlay = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay)
title_ff_label = QtWidgets.QLabel("<b>%s</b>" % _('FreeForm:'))
title_ff_label = QtWidgets.QLabel("<b>%s:</b>" % _('FreeForm'))
title_ff_label.setToolTip(
_("The cutout shape can be of ny shape.\n"
"Useful when the PCB has a non-rectangular shape.")
@ -191,7 +192,7 @@ class CutOut(FlatCAMTool):
hlay2 = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay2)
title_rct_label = QtWidgets.QLabel("<b>%s</b>" % _('Rectangular:'))
title_rct_label = QtWidgets.QLabel("<b>%s:</b>" % _('Rectangular'))
title_rct_label.setToolTip(
_("The resulting cutout shape is\n"
"always a rectangle shape and it will be\n"
@ -228,7 +229,7 @@ class CutOut(FlatCAMTool):
self.man_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.man_object_combo.setCurrentIndex(1)
self.man_object_label = QtWidgets.QLabel(_("Geo Obj:"))
self.man_object_label = QtWidgets.QLabel('%s:' % _("Geo Obj"))
self.man_object_label.setToolTip(
_("Geometry object used to create the manual cutout.")
)
@ -241,7 +242,7 @@ class CutOut(FlatCAMTool):
hlay3 = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay3)
self.man_geo_label = QtWidgets.QLabel(_("Manual Geo:"))
self.man_geo_label = QtWidgets.QLabel('%s:' % _("Manual Geo"))
self.man_geo_label.setToolTip(
_("If the object to be cutout is a Gerber\n"
"first create a Geometry that surrounds it,\n"
@ -263,7 +264,7 @@ class CutOut(FlatCAMTool):
hlay4 = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay4)
self.man_bridge_gaps_label = QtWidgets.QLabel(_("Manual Add Bridge Gaps:"))
self.man_bridge_gaps_label = QtWidgets.QLabel('%s:' % _("Manual Add Bridge Gaps"))
self.man_bridge_gaps_label.setToolTip(
_("Use the left mouse button (LMB) click\n"
"to create a bridge gap to separate the PCB from\n"
@ -292,6 +293,9 @@ class CutOut(FlatCAMTool):
self.flat_geometry = []
# this is the Geometry object generated in this class to be used for adding manual gaps
self.man_cutout_obj = None
# Signals
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
@ -315,7 +319,12 @@ class CutOut(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
@ -410,8 +419,9 @@ class CutOut(FlatCAMTool):
self.app.inform.emit(_("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry."))
return
if gaps not in ['LR', 'TB', '2LR', '2TB', '4', '8']:
self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: "
"'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. "
"Fill in a correct value and retry. "))
return
@ -446,44 +456,46 @@ class CutOut(FlatCAMTool):
leny = (ymax - ymin) + (margin * 2)
proc_geometry = []
if gaps == 'None':
pass
else:
if gaps == '8' or gaps == '2LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + leny / 4) # topright_y
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize - leny / 4,
xmax + gapsize,
py + gapsize - leny / 4)
if gaps == '8' or gaps == '2LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + leny / 4) # topright_y
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize - leny / 4,
xmax + gapsize,
py + gapsize - leny / 4)
if gaps == '8' or gaps == '2TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize + lenx / 4,
ymin - gapsize,
px + gapsize + lenx / 4,
ymax + gapsize)
geom = self.subtract_poly_from_geo(geom,
px - gapsize - lenx / 4,
ymin - gapsize,
px + gapsize - lenx / 4,
ymax + gapsize)
if gaps == '8' or gaps == '2TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize + lenx / 4,
ymin - gapsize,
px + gapsize + lenx / 4,
ymax + gapsize)
geom = self.subtract_poly_from_geo(geom,
px - gapsize - lenx / 4,
ymin - gapsize,
px + gapsize - lenx / 4,
ymax + gapsize)
if gaps == '4' or gaps == 'LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps == '4' or gaps == 'LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps == '4' or gaps == 'TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
if gaps == '4' or gaps == 'TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
try:
for g in geom:
@ -603,8 +615,9 @@ class CutOut(FlatCAMTool):
self.app.inform.emit(_("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry."))
return
if gaps not in ['LR', 'TB', '2LR', '2TB', '4', '8']:
self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: "
"'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. "
"Fill in a correct value and retry. "))
return
@ -630,43 +643,46 @@ class CutOut(FlatCAMTool):
lenx = (xmax - xmin) + (margin * 2)
leny = (ymax - ymin) + (margin * 2)
if gaps == '8' or gaps == '2LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + leny / 4) # topright_y
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize - leny / 4,
xmax + gapsize,
py + gapsize - leny / 4)
if gaps == 'None':
pass
else:
if gaps == '8' or gaps == '2LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + leny / 4) # topright_y
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize - leny / 4,
xmax + gapsize,
py + gapsize - leny / 4)
if gaps == '8' or gaps == '2TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize + lenx / 4,
ymin - gapsize,
px + gapsize + lenx / 4,
ymax + gapsize)
geom = self.subtract_poly_from_geo(geom,
px - gapsize - lenx / 4,
ymin - gapsize,
px + gapsize - lenx / 4,
ymax + gapsize)
if gaps == '8' or gaps == '2TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize + lenx / 4,
ymin - gapsize,
px + gapsize + lenx / 4,
ymax + gapsize)
geom = self.subtract_poly_from_geo(geom,
px - gapsize - lenx / 4,
ymin - gapsize,
px + gapsize - lenx / 4,
ymax + gapsize)
if gaps == '4' or gaps == 'LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps == '4' or gaps == 'LR':
geom = self.subtract_poly_from_geo(geom,
xmin - gapsize,
py - gapsize,
xmax + gapsize,
py + gapsize)
if gaps == '4' or gaps == 'TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
if gaps == '4' or gaps == 'TB':
geom = self.subtract_poly_from_geo(geom,
px - gapsize,
ymin - gapsize,
px + gapsize,
ymax + gapsize)
try:
for g in geom:
proc_geometry.append(g)
@ -743,6 +759,15 @@ class CutOut(FlatCAMTool):
"Add it and retry."))
return
name = self.man_object_combo.currentText()
# Get Geometry source object to be used as target for Manual adding Gaps
try:
self.man_cutout_obj = self.app.collection.get_by_name(str(name))
except Exception as e:
log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
return "Could not retrieve object: %s" % name
self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
@ -770,29 +795,43 @@ class CutOut(FlatCAMTool):
self.app.geo_editor.tool_shape.clear(update=True)
self.app.geo_editor.tool_shape.enabled = False
self.gapFinished.emit()
# if RMB then we exit
elif event.button == 2:
self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
self.app.plotcanvas.vis_disconnect('mouse_release', self.doit)
self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
# Remove any previous utility shape
self.app.geo_editor.tool_shape.clear(update=True)
self.app.geo_editor.tool_shape.enabled = False
def on_manual_cutout(self, click_pos):
name = self.man_object_combo.currentText()
# Get source object.
try:
cutout_obj = self.app.collection.get_by_name(str(name))
self.man_cutout_obj = self.app.collection.get_by_name(str(name))
except Exception as e:
log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
return "Could not retrieve object: %s" % name
if cutout_obj is None:
self.app.inform.emit(_("[ERROR_NOTCL] Geometry object for manual cutout not found: %s") % cutout_obj)
if self.man_cutout_obj is None:
self.app.inform.emit(
_("[ERROR_NOTCL] Geometry object for manual cutout not found: %s") % self.man_cutout_obj)
return
# use the snapped position as reference
snapped_pos = self.app.geo_editor.snap(click_pos[0], click_pos[1])
cut_poly = self.cutting_geo(pos=(snapped_pos[0], snapped_pos[1]))
cutout_obj.subtract_polygon(cut_poly)
self.man_cutout_obj.subtract_polygon(cut_poly)
cutout_obj.plot()
self.man_cutout_obj.plot()
self.app.inform.emit(_("[success] Added manual Bridge Gap."))
self.app.should_we_save = True
@ -864,9 +903,17 @@ class CutOut(FlatCAMTool):
geo = geo_union.convex_hull
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
elif kind == 'single':
x0, y0, x1, y1 = geo_union.bounds
geo = box(x0, y0, x1, y1)
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
if isinstance(geo_union, Polygon) or \
(isinstance(geo_union, list) and len(geo_union) == 1) or \
(isinstance(geo_union, MultiPolygon) and len(geo_union) == 1):
geo_obj.solid_geometry = geo_union.buffer(margin + abs(dia / 2)).exterior
elif isinstance(geo_union, MultiPolygon):
x0, y0, x1, y1 = geo_union.bounds
geo = box(x0, y0, x1, y1)
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
else:
self.app.inform.emit(_("[ERROR_NOTCL] Geometry not supported for cutout: %s") % type(geo_union))
return 'fail'
else:
geo = geo_union
geo = geo.buffer(margin + abs(dia / 2))
@ -911,11 +958,67 @@ class CutOut(FlatCAMTool):
snap_x, snap_y = self.app.geo_editor.snap(x, y)
geo = self.cutting_geo(pos=(snap_x, snap_y))
# #################################################
# ### This section makes the cutting geo to #######
# ### rotate if it intersects the target geo ######
# #################################################
cut_geo = self.cutting_geo(pos=(snap_x, snap_y))
man_geo = self.man_cutout_obj.solid_geometry
def get_angle(geo):
line = cut_geo.intersection(geo)
try:
pt1_x = line.coords[0][0]
pt1_y = line.coords[0][1]
pt2_x = line.coords[1][0]
pt2_y = line.coords[1][1]
dx = pt1_x - pt2_x
dy = pt1_y - pt2_y
if dx == 0 or dy == 0:
angle = 0
else:
radian = math.atan(dx / dy)
angle = radian * 180 / math.pi
except Exception as e:
angle = 0
return angle
try:
rot_angle = 0
for geo_el in man_geo:
if isinstance(geo_el, Polygon):
work_geo = geo_el.exterior
if cut_geo.intersects(work_geo):
rot_angle = get_angle(geo=work_geo)
else:
rot_angle = 0
else:
rot_angle = 0
if cut_geo.intersects(geo_el):
rot_angle = get_angle(geo=geo_el)
if rot_angle != 0:
break
except TypeError:
if isinstance(man_geo, Polygon):
work_geo = man_geo.exterior
if cut_geo.intersects(work_geo):
rot_angle = get_angle(geo=work_geo)
else:
rot_angle = 0
else:
rot_angle = 0
if cut_geo.intersects(man_geo):
rot_angle = get_angle(geo=man_geo)
# rotate only if there is an angle to rotate to
if rot_angle != 0:
cut_geo = affinity.rotate(cut_geo, -rot_angle)
# Remove any previous utility shape
self.app.geo_editor.tool_shape.clear(update=True)
self.draw_utility_geometry(geo=geo)
self.draw_utility_geometry(geo=cut_geo)
def draw_utility_geometry(self, geo):
self.app.geo_editor.tool_shape.add(

View File

@ -44,7 +44,7 @@ class DblSidedTool(FlatCAMTool):
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(1)
self.botlay_label = QtWidgets.QLabel(_("<b>GERBER:</b>"))
self.botlay_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.botlay_label.setToolTip(
"Gerber to be mirrored."
)
@ -68,7 +68,7 @@ class DblSidedTool(FlatCAMTool):
self.exc_object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.exc_object_combo.setCurrentIndex(1)
self.excobj_label = QtWidgets.QLabel(_("<b>EXCELLON:</b>"))
self.excobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("EXCELLON"))
self.excobj_label.setToolTip(
_("Excellon Object to be mirrored.")
)
@ -92,7 +92,7 @@ class DblSidedTool(FlatCAMTool):
self.geo_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.geo_object_combo.setCurrentIndex(1)
self.geoobj_label = QtWidgets.QLabel(_("<b>GEOMETRY</b>:"))
self.geoobj_label = QtWidgets.QLabel("<b>%s</b>:" % _("GEOMETRY"))
self.geoobj_label.setToolTip(
_("Geometry Obj to be mirrored.")
)
@ -149,7 +149,7 @@ class DblSidedTool(FlatCAMTool):
# ## Point/Box
self.point_box_container = QtWidgets.QVBoxLayout()
self.pb_label = QtWidgets.QLabel("<b>%s</b>" % _('Point/Box Reference:'))
self.pb_label = QtWidgets.QLabel("<b>%s:</b>" % _('Point/Box Reference'))
self.pb_label.setToolTip(
_("If 'Point' is selected above it store the coordinates (x, y) through which\n"
"the mirroring axis passes.\n"
@ -189,7 +189,7 @@ class DblSidedTool(FlatCAMTool):
self.box_combo_type.hide()
# ## Alignment holes
self.ah_label = QtWidgets.QLabel("<b>%s</b>" % _('Alignment Drill Coordinates:'))
self.ah_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Coordinates'))
self.ah_label.setToolTip(
_("Alignment holes (x1, y1), (x2, y2), ... "
"on one side of the mirror axis. For each set of (x, y) coordinates\n"
@ -220,7 +220,7 @@ class DblSidedTool(FlatCAMTool):
grid_lay3.addWidget(self.add_drill_point_button, 0, 1)
# ## Drill diameter for alignment holes
self.dt_label = QtWidgets.QLabel("<b>%s</b>:" % _('Alignment Drill Diameter'))
self.dt_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Diameter'))
self.dt_label.setToolTip(
_("Diameter of the drill for the "
"alignment holes.")
@ -231,7 +231,7 @@ class DblSidedTool(FlatCAMTool):
self.layout.addLayout(hlay)
self.drill_dia = FCEntry()
self.dd_label = QtWidgets.QLabel(_("Drill diam.:"))
self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
self.dd_label.setToolTip(
_("Diameter of the drill for the "
"alignment holes.")
@ -288,7 +288,12 @@ class DblSidedTool(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -53,7 +53,7 @@ class Film(FlatCAMTool):
self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
self.tf_type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.tf_type_obj_combo_label = QtWidgets.QLabel(_("Object Type:"))
self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.tf_type_obj_combo_label.setToolTip(
_("Specify the type of object for which to create the film.\n"
"The object can be of type: Gerber or Geometry.\n"
@ -68,7 +68,7 @@ class Film(FlatCAMTool):
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.tf_object_combo.setCurrentIndex(1)
self.tf_object_label = QtWidgets.QLabel(_("Film Object:"))
self.tf_object_label = QtWidgets.QLabel('%s:' % _("Film Object"))
self.tf_object_label.setToolTip(
_("Object for which to create the film.")
)
@ -101,7 +101,7 @@ class Film(FlatCAMTool):
self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.tf_box_combo.setCurrentIndex(1)
self.tf_box_combo_label = QtWidgets.QLabel(_("Box Object:"))
self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
self.tf_box_combo_label.setToolTip(
_("The actual object that is used a container for the\n "
"selected object for which we create the film.\n"
@ -127,7 +127,7 @@ class Film(FlatCAMTool):
# Boundary for negative film generation
self.boundary_entry = FCEntry()
self.boundary_label = QtWidgets.QLabel(_("Border:"))
self.boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
self.boundary_label.setToolTip(
_("Specify a border around the object.\n"
"Only for negative film.\n"
@ -141,7 +141,7 @@ class Film(FlatCAMTool):
tf_form_layout.addRow(self.boundary_label, self.boundary_entry)
self.film_scale_entry = FCEntry()
self.film_scale_label = QtWidgets.QLabel(_("Scale Stroke:"))
self.film_scale_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
self.film_scale_label.setToolTip(
_("Scale the line stroke thickness of each feature in the SVG file.\n"
"It means that the line that envelope each SVG feature will be thicker or thinner,\n"
@ -190,7 +190,12 @@ class Film(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -50,7 +50,7 @@ class ToolImage(FlatCAMTool):
self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
self.tf_type_obj_combo.setItemIcon(1, QtGui.QIcon("share/geometry16.png"))
self.tf_type_obj_combo_label = QtWidgets.QLabel(_("Object Type:"))
self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.tf_type_obj_combo_label.setToolTip(
_("Specify the type of object to create from the image.\n"
"It can be of type: Gerber or Geometry.")
@ -60,7 +60,7 @@ class ToolImage(FlatCAMTool):
# DPI value of the imported image
self.dpi_entry = IntEntry()
self.dpi_label = QtWidgets.QLabel(_("DPI value:"))
self.dpi_label = QtWidgets.QLabel('%s:' % _("DPI value"))
self.dpi_label.setToolTip(
_("Specify a DPI value for the image.")
)
@ -69,7 +69,7 @@ class ToolImage(FlatCAMTool):
self.emty_lbl = QtWidgets.QLabel("")
self.layout.addWidget(self.emty_lbl)
self.detail_label = QtWidgets.QLabel("<font size=4><b>%s:</b>" % _('Level of detail'))
self.detail_label = QtWidgets.QLabel("<font size=4><b>%s:</b></font>" % _('Level of detail'))
self.layout.addWidget(self.detail_label)
ti2_form_layout = QtWidgets.QFormLayout()
@ -157,7 +157,12 @@ class ToolImage(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -40,7 +40,7 @@ class Measurement(FlatCAMTool):
form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout)
self.units_label = QtWidgets.QLabel(_("Units:"))
self.units_label = QtWidgets.QLabel('%s:' % _("Units"))
self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
self.units_value = QtWidgets.QLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
self.units_value.setDisabled(True)
@ -51,10 +51,10 @@ class Measurement(FlatCAMTool):
self.stop_label = QtWidgets.QLabel("<b>%s</b> %s:" % (_('Stop'), _('Coords')))
self.stop_label.setToolTip(_("This is the measuring Stop point coordinates."))
self.distance_x_label = QtWidgets.QLabel(_("Dx:"))
self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
self.distance_y_label = QtWidgets.QLabel(_("Dy:"))
self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))

View File

@ -10,10 +10,10 @@ from FlatCAMTool import FlatCAMTool
from copy import copy, deepcopy
from ObjectCollection import *
import time
from shapely.geometry import base
import gettext
import FlatCAMTranslation as fcTranslate
from shapely.geometry import base
import builtins
fcTranslate.apply_language('strings')
@ -53,22 +53,46 @@ class NonCopperClear(FlatCAMTool, Gerber):
form_layout = QtWidgets.QFormLayout()
self.tools_box.addLayout(form_layout)
# ## Object
self.object_combo = QtWidgets.QComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
# ################################################
# ##### Type of object to be copper cleaned ######
# ################################################
self.type_obj_combo = QtWidgets.QComboBox()
self.type_obj_combo.addItem("Gerber")
self.type_obj_combo.addItem("Excellon")
self.type_obj_combo.addItem("Geometry")
self.object_label = QtWidgets.QLabel("Gerber:")
self.object_label.setToolTip(
_("Gerber object to be cleared of excess copper. ")
# we get rid of item1 ("Excellon") as it is not suitable
self.type_obj_combo.view().setRowHidden(1, True)
self.type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be cleared of excess copper.\n"
"It can be of type: Gerber or Geometry.\n"
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
e_lab_0 = QtWidgets.QLabel('')
self.type_obj_combo_label.setMinimumWidth(60)
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
form_layout.addRow(self.object_label, self.object_combo)
# ################################################
# ##### The object to be copper cleaned ##########
# ################################################
self.obj_combo = QtWidgets.QComboBox()
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(_("Object to be cleared of excess copper."))
form_layout.addRow(self.object_label, self.obj_combo)
e_lab_0 = QtWidgets.QLabel('')
form_layout.addRow(e_lab_0)
#### Tools ## ##
# ### Tools ## ##
self.tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Tools Table'))
self.tools_table_label.setToolTip(
_("Tools pool from which the algorithm\n"
@ -110,23 +134,35 @@ class NonCopperClear(FlatCAMTool, Gerber):
"Choosing the <B>V-Shape</B> Tool Type automatically will select the Operation Type "
"in the resulting geometry as Isolation."))
self.empty_label = QtWidgets.QLabel('')
self.tools_box.addWidget(self.empty_label)
self.ncc_order_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tool order'))
self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
"'No' --> means that the used order is the one in the tool table\n"
"'Forward' --> means that the tools will be ordered from small to big\n"
"'Reverse' --> menas that the tools will ordered from big to small\n\n"
"WARNING: using rest machining will automatically set the order\n"
"in reverse and disable this control."))
self.ncc_order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
{'label': _('Forward'), 'value': 'fwd'},
{'label': _('Reverse'), 'value': 'rev'}])
self.ncc_order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
"'No' --> means that the used order is the one in the tool table\n"
"'Forward' --> means that the tools will be ordered from small to big\n"
"'Reverse' --> menas that the tools will ordered from big to small\n\n"
"WARNING: using rest machining will automatically set the order\n"
"in reverse and disable this control."))
form = QtWidgets.QFormLayout()
self.tools_box.addLayout(form)
form.addRow(QtWidgets.QLabel(''), QtWidgets.QLabel(''))
form.addRow(self.ncc_order_label, self.ncc_order_radio)
# ### Add a new Tool ####
hlay = QtWidgets.QHBoxLayout()
self.tools_box.addLayout(hlay)
self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Tool Dia'))
self.addtool_entry_lbl.setToolTip(
_("Diameter for the new tool to add in the Tool Table")
)
self.addtool_entry = FCEntry2()
# hlay.addWidget(self.addtool_label)
# hlay.addStretch()
hlay.addWidget(self.addtool_entry_lbl)
hlay.addWidget(self.addtool_entry)
form.addRow(self.addtool_entry_lbl, self.addtool_entry)
grid2 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid2)
@ -159,7 +195,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid3)
e_lab_1 = QtWidgets.QLabel('')
e_lab_1 = QtWidgets.QLabel('<b>%s:</b>' % _("Parameters"))
grid3.addWidget(e_lab_1, 0, 0)
nccoverlabel = QtWidgets.QLabel(_('Overlap Rate:'))
@ -178,7 +214,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_overlap_entry = FCEntry()
grid3.addWidget(self.ncc_overlap_entry, 1, 1)
nccmarginlabel = QtWidgets.QLabel(_('Margin:'))
nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
nccmarginlabel.setToolTip(
_("Bounding box margin.")
)
@ -187,7 +223,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3.addWidget(self.ncc_margin_entry, 2, 1)
# Method
methodlabel = QtWidgets.QLabel(_('Method:'))
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
methodlabel.setToolTip(
_("Algorithm for non-copper clearing:<BR>"
"<B>Standard</B>: Fixed step inwards.<BR>"
@ -203,7 +239,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3.addWidget(self.ncc_method_radio, 3, 1)
# Connect lines
pathconnectlabel = QtWidgets.QLabel(_("Connect:"))
pathconnectlabel = QtWidgets.QLabel('%s:' % _("Connect"))
pathconnectlabel.setToolTip(
_("Draw lines between resulting\n"
"segments to minimize tool lifts.")
@ -212,7 +248,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_connect_cb = FCCheckBox()
grid3.addWidget(self.ncc_connect_cb, 4, 1)
contourlabel = QtWidgets.QLabel(_("Contour:"))
contourlabel = QtWidgets.QLabel('%s:' % _("Contour"))
contourlabel.setToolTip(
_("Cut around the perimeter of the polygon\n"
"to trim rough edges.")
@ -221,7 +257,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_contour_cb = FCCheckBox()
grid3.addWidget(self.ncc_contour_cb, 5, 1)
restlabel = QtWidgets.QLabel(_("Rest M.:"))
restlabel = QtWidgets.QLabel('%s:' % _("Rest M."))
restlabel.setToolTip(
_("If checked, use 'rest machining'.\n"
"Basically it will clear copper outside PCB features,\n"
@ -236,7 +272,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3.addWidget(self.ncc_rest_cb, 6, 1)
# ## NCC Offset choice
self.ncc_offset_choice_label = QtWidgets.QLabel(_("Offset:"))
self.ncc_offset_choice_label = QtWidgets.QLabel('%s:' % _("Offset"))
self.ncc_offset_choice_label.setToolTip(
_("If used, it will add an offset to the copper features.\n"
"The copper clearing will finish to a distance\n"
@ -248,7 +284,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3.addWidget(self.ncc_choice_offset_cb, 7, 1)
# ## NCC Offset value
self.ncc_offset_label = QtWidgets.QLabel(_("Offset value:"))
self.ncc_offset_label = QtWidgets.QLabel('%s:' % _("Offset value"))
self.ncc_offset_label.setToolTip(
_("If used, it will add an offset to the copper features.\n"
"The copper clearing will finish to a distance\n"
@ -273,22 +309,27 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_offset_spinner.hide()
# ## Reference
self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
{'label': _('Box'), 'value': 'box'}])
self.reference_radio = RadioSet([
{'label': _('Itself'), 'value': 'itself'},
{"label": _("Area Selection"), "value": "area"},
{'label': _("Reference Object"), 'value': 'box'}
], orientation='vertical', stretch=False)
self.reference_label = QtWidgets.QLabel(_("Reference:"))
self.reference_label.setToolTip(
_("- 'Itself': the non copper clearing extent\n"
_("- 'Itself' - the non copper clearing extent\n"
"is based on the object that is copper cleared.\n "
"- 'Box': will do non copper clearing within the box\n"
"specified by the object selected in the Ref. Object combobox.")
"- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
"- 'Reference Object' - will do non copper clearing within the area\n"
"specified by another object.")
)
grid3.addWidget(self.reference_label, 9, 0)
grid3.addWidget(self.reference_radio, 9, 1)
grid4 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid4)
form1 = QtWidgets.QFormLayout()
self.tools_box.addLayout(form1)
self.box_combo_type_label = QtWidgets.QLabel(_("Ref. Type:"))
self.box_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
self.box_combo_type_label.setToolTip(
_("The type of FlatCAM object to be used as non copper clearing reference.\n"
"It can be Gerber, Excellon or Geometry.")
@ -297,11 +338,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.box_combo_type.addItem(_("Gerber Reference Box Object"))
self.box_combo_type.addItem(_("Excellon Reference Box Object"))
self.box_combo_type.addItem(_("Geometry Reference Box Object"))
form1.addRow(self.box_combo_type_label, self.box_combo_type)
grid4.addWidget(self.box_combo_type_label, 0, 0)
grid4.addWidget(self.box_combo_type, 0, 1)
self.box_combo_label = QtWidgets.QLabel(_("Ref. Object:"))
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
self.box_combo_label.setToolTip(
_("The FlatCAM object to be used as non copper clearing reference.")
)
@ -309,8 +348,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
grid4.addWidget(self.box_combo_label, 1, 0)
grid4.addWidget(self.box_combo, 1, 1)
form1.addRow(self.box_combo_label, self.box_combo)
self.box_combo.hide()
self.box_combo_label.hide()
@ -323,6 +361,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
"for non-copper routing.")
)
self.tools_box.addWidget(self.generate_ncc_button)
self.tools_box.addStretch()
self.units = ''
self.ncc_tools = {}
@ -333,19 +372,32 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.obj_name = ""
self.ncc_obj = None
self.sel_rect = []
self.bound_obj_name = ""
self.bound_obj = None
self.tools_box.addStretch()
self.first_click = False
self.cursor_pos = None
self.mouse_is_dragging = False
self.addtool_btn.clicked.connect(self.on_tool_add)
self.addtool_entry.returnPressed.connect(self.on_tool_add)
self.deltool_btn.clicked.connect(self.on_tool_delete)
self.generate_ncc_button.clicked.connect(self.on_ncc)
self.generate_ncc_button.clicked.connect(self.on_ncc_click)
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.reference_radio.group_toggle_fn = self.on_toggle_reference
self.ncc_choice_offset_cb.stateChanged.connect(self.on_offset_choice)
self.ncc_rest_cb.stateChanged.connect(self.on_rest_machining_check)
self.ncc_order_radio.activated_custom[str].connect(self.on_order_changed)
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
def on_type_obj_index_changed(self, index):
obj_type = self.type_obj_combo.currentIndex()
self.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(0)
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+N', **kwargs)
@ -360,7 +412,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
@ -382,6 +439,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
def set_tool_ui(self):
self.tools_frame.show()
self.ncc_order_radio.set_value(self.app.defaults["tools_nccorder"])
self.ncc_overlap_entry.set_value(self.app.defaults["tools_nccoverlap"])
self.ncc_margin_entry.set_value(self.app.defaults["tools_nccmargin"])
self.ncc_method_radio.set_value(self.app.defaults["tools_nccmethod"])
@ -484,7 +542,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
sorted_tools = []
for k, v in self.ncc_tools.items():
sorted_tools.append(float('%.4f' % float(v['tooldia'])))
sorted_tools.sort()
order = self.ncc_order_radio.get_value()
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
n = len(sorted_tools)
self.tools_table.setRowCount(n)
@ -570,7 +635,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.box_combo.setCurrentIndex(0)
def on_toggle_reference(self):
if self.reference_radio.get_value() == "itself":
if self.reference_radio.get_value() == "itself" or self.reference_radio.get_value() == "area":
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
@ -589,6 +654,19 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_offset_label.hide()
self.ncc_offset_spinner.hide()
def on_order_changed(self, order):
if order != 'no':
self.build_ui()
def on_rest_machining_check(self, state):
if state:
self.ncc_order_radio.set_value('rev')
self.ncc_order_label.setDisabled(True)
self.ncc_order_radio.setDisabled(True)
else:
self.ncc_order_label.setDisabled(False)
self.ncc_order_radio.setDisabled(False)
def on_tool_add(self, dia=None, muted=None):
self.ui_disconnect()
@ -743,10 +821,128 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.app.inform.emit(_("[success] Tool(s) deleted from Tool Table."))
self.build_ui()
def on_ncc(self):
def on_ncc_click(self):
self.bound_obj = None
self.ncc_obj = None
ref_choice = self.reference_radio.get_value()
if ref_choice == 'itself':
self.bound_obj_name = self.object_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
self.on_ncc()
elif ref_choice == 'box':
self.bound_obj_name = self.box_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.bound_obj_name)
return "Could not retrieve object: %s. Error: %s" % (self.bound_obj_name, str(e))
self.on_ncc()
else:
self.app.inform.emit(_("[WARNING_NOTCL] Click the start point of the area."))
# use the first tool in the tool table; get the diameter
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
# To be called after clicking on the plot.
def on_mouse_release(event):
# do paint single only for left mouse clicks
if event.button == 1:
if self.first_click is False:
self.first_click = True
self.app.inform.emit(_("[WARNING_NOTCL] Click the end point of the paint area."))
self.cursor_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
if self.app.grid_status() == True:
self.cursor_pos = self.app.geo_editor.snap(self.cursor_pos[0], self.cursor_pos[1])
else:
self.app.inform.emit(_("Zone added. Right click to finish."))
self.app.delete_selection_shape()
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
if self.app.grid_status() == True:
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
x1, y1 = curr_pos[0], curr_pos[1]
pt1 = (x0, y0)
pt2 = (x1, y0)
pt3 = (x1, y1)
pt4 = (x0, y1)
self.sel_rect.append(Polygon([pt1, pt2, pt3, pt4]))
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif modifiers == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
mod_key = None
if mod_key == self.app.defaults["global_mselect_key"]:
self.first_click = False
return
self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.on_ncc()
elif event.button == 2 and self.first_click is False and self.mouse_is_dragging is False:
self.first_click = False
self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.on_ncc()
# called on mouse move
def on_mouse_move(event):
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
self.app.app_cursor.enabled = False
if event.button == 2:
if event.is_dragging is True:
self.mouse_is_dragging = True
else:
self.mouse_is_dragging = False
if self.app.grid_status() == True:
self.app.app_cursor.enabled = True
# Update cursor
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
symbol='++', edge_color='black', size=20)
if self.first_click:
self.app.delete_selection_shape()
self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
coords=(curr_pos[0], curr_pos[1]),
face_alpha=0.0)
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_connect('mouse_move', on_mouse_move)
def on_ncc(self):
try:
over = float(self.ncc_overlap_entry.get_value())
except ValueError:
@ -797,24 +993,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
pol_method = self.ncc_method_radio.get_value()
pol_method = pol_method if pol_method else self.app.defaults["tools_nccmethod"]
if self.reference_radio.get_value() == 'itself':
self.bound_obj_name = self.object_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
else:
self.bound_obj_name = self.box_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
self.obj_name = self.object_combo.currentText()
self.obj_name = self.obj_combo.currentText()
# Get source object.
try:
self.ncc_obj = self.app.collection.get_by_name(self.obj_name)
@ -823,26 +1002,52 @@ class NonCopperClear(FlatCAMTool, Gerber):
return "Could not retrieve object: %s" % self.obj_name
# Prepare non-copper polygons
try:
if not isinstance(self.bound_obj.solid_geometry, MultiPolygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
else:
env_obj = self.bound_obj.solid_geometry.convex_hull
bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("NonCopperClear.on_ncc() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
if self.reference_radio.get_value() == 'area':
geo_n = self.sel_rect
geo_buff_list = []
for poly in geo_n:
geo_buff_list.append(poly.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre))
bounding_box = cascaded_union(geo_buff_list)
else:
geo_n = self.bound_obj.solid_geometry
try:
if isinstance(geo_n, MultiPolygon):
env_obj = geo_n.convex_hull
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
(isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
env_obj = cascaded_union(geo_n)
else:
env_obj = cascaded_union(geo_n)
env_obj = env_obj.convex_hull
bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("NonCopperClear.on_ncc() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
# calculate the empty area by subtracting the solid_geometry from the object bounding box geometry
if self.ncc_choice_offset_cb.isChecked():
self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
offseted_geo = self.ncc_obj.solid_geometry.buffer(distance=ncc_offset_value)
self.app.inform.emit(_("[success] Buffering finished ..."))
empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
if isinstance(self.ncc_obj, FlatCAMGerber):
if self.ncc_choice_offset_cb.isChecked():
self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
offseted_geo = self.ncc_obj.solid_geometry.buffer(distance=ncc_offset_value)
self.app.inform.emit(_("[success] Buffering finished ..."))
empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
else:
empty = self.get_ncc_empty_area(target=self.ncc_obj.solid_geometry, boundary=bounding_box)
elif isinstance(self.ncc_obj, FlatCAMGeometry):
sol_geo = cascaded_union(self.ncc_obj.solid_geometry)
if self.ncc_choice_offset_cb.isChecked():
self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
offseted_geo = sol_geo.buffer(distance=ncc_offset_value)
self.app.inform.emit(_("[success] Buffering finished ..."))
empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
else:
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
else:
empty = self.get_ncc_empty_area(target=self.ncc_obj.solid_geometry, boundary=bounding_box)
self.inform.emit(_('[ERROR_NOTCL] The selected object is not suitable for copper clearing.'))
return
if type(empty) is Polygon:
empty = MultiPolygon([empty])
@ -878,7 +1083,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
sorted_tools = []
for k, v in self.ncc_tools.items():
sorted_tools.append(float('%.4f' % float(v['tooldia'])))
sorted_tools.sort(reverse=True)
order = self.ncc_order_radio.get_value()
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
# Do job in background
proc = self.app.proc_container.new(_("Clearing Non-Copper areas."))
@ -897,6 +1109,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
# Generate area for each tool
offset = sum(sorted_tools)
current_uid = int(1)
tool = eval(self.app.defaults["tools_ncctools"])[0]
for tool in sorted_tools:
self.app.inform.emit(_('[success] Non-Copper Clearing with ToolDia = %s started.') % str(tool))
@ -982,8 +1195,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
# focus on Selected Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
self.tools_frame.hide()
self.app.ui.notebook.setTabText(2, _("Tools"))
# Promise object with the new name
self.app.collection.promise(name)
@ -1013,6 +1224,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
cleared_by_last_tool = []
rest_geo = []
current_uid = 1
tool = eval(self.app.defaults["tools_ncctools"])[0]
# repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
app_obj.poly_not_cleared = True
@ -1135,9 +1347,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
# reset the variable for next use
app_obj.poly_not_cleared = False
self.tools_frame.hide()
app_obj.ui.notebook.setTabText(2, "Tools")
# Promise object with the new name
self.app.collection.promise(name)
@ -1157,3 +1366,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
def reset_fields(self):
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
def reset_usage(self):
self.obj_name = ""
self.ncc_obj = None
self.bound_obj = None
self.first_click = False
self.cursor_pos = None
self.mouse_is_dragging = False
self.sel_rect = []

View File

@ -1,14 +1,15 @@
# ########################################################## ##
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# File Modified: Marius Adrian Stanciu (c) #
# Date: 3/10/2019 #
# MIT Licence #
# ########################################################## ##
# ##########################################################
from FlatCAMTool import FlatCAMTool
from copy import copy, deepcopy
from ObjectCollection import *
from shapely.geometry import base
import gettext
import FlatCAMTranslation as fcTranslate
@ -21,7 +22,7 @@ if '_' not in builtins.__dict__:
class ToolPaint(FlatCAMTool, Gerber):
toolName = _("Paint Area")
toolName = _("Paint Tool")
def __init__(self, app):
self.app = app
@ -51,18 +52,43 @@ class ToolPaint(FlatCAMTool, Gerber):
form_layout = QtWidgets.QFormLayout()
self.tools_box.addLayout(form_layout)
# ## Object
self.object_combo = QtWidgets.QComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
# ################################################
# ##### Type of object to be painted #############
# ################################################
self.type_obj_combo = QtWidgets.QComboBox()
self.type_obj_combo.addItem("Gerber")
self.type_obj_combo.addItem("Excellon")
self.type_obj_combo.addItem("Geometry")
self.object_label = QtWidgets.QLabel(_("Geometry:"))
self.object_label.setToolTip(
_("Geometry object to be painted. ")
# we get rid of item1 ("Excellon") as it is not suitable
self.type_obj_combo.view().setRowHidden(1, True)
self.type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be painted.\n"
"It can be of type: Gerber or Geometry.\n"
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
self.type_obj_combo_label.setMinimumWidth(60)
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
# ################################################
# ##### The object to be painted #################
# ################################################
self.obj_combo = QtWidgets.QComboBox()
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(_("Object to be painted."))
form_layout.addRow(self.object_label, self.obj_combo)
e_lab_0 = QtWidgets.QLabel('')
form_layout.addRow(self.object_label, self.object_combo)
form_layout.addRow(e_lab_0)
# ### Tools ## ##
@ -107,8 +133,27 @@ class ToolPaint(FlatCAMTool, Gerber):
"Choosing the <B>V-Shape</B> Tool Type automatically will select the Operation Type "
"in the resulting geometry as Isolation."))
self.empty_label = QtWidgets.QLabel('')
self.tools_box.addWidget(self.empty_label)
self.order_label = QtWidgets.QLabel('<b>%s:</b>' % _('Tool order'))
self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
"'No' --> means that the used order is the one in the tool table\n"
"'Forward' --> means that the tools will be ordered from small to big\n"
"'Reverse' --> menas that the tools will ordered from big to small\n\n"
"WARNING: using rest machining will automatically set the order\n"
"in reverse and disable this control."))
self.order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
{'label': _('Forward'), 'value': 'fwd'},
{'label': _('Reverse'), 'value': 'rev'}])
self.order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
"'No' --> means that the used order is the one in the tool table\n"
"'Forward' --> means that the tools will be ordered from small to big\n"
"'Reverse' --> menas that the tools will ordered from big to small\n\n"
"WARNING: using rest machining will automatically set the order\n"
"in reverse and disable this control."))
form = QtWidgets.QFormLayout()
self.tools_box.addLayout(form)
form.addRow(QtWidgets.QLabel(''), QtWidgets.QLabel(''))
form.addRow(self.order_label, self.order_radio)
# ### Add a new Tool ## ##
hlay = QtWidgets.QHBoxLayout()
@ -157,7 +202,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.tools_box.addLayout(grid3)
# Overlap
ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
ovlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate'))
ovlabel.setToolTip(
_("How much (fraction) of the tool width to overlap each tool pass.\n"
"Example:\n"
@ -174,7 +219,7 @@ class ToolPaint(FlatCAMTool, Gerber):
grid3.addWidget(self.paintoverlap_entry, 1, 1)
# Margin
marginlabel = QtWidgets.QLabel(_('Margin:'))
marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
marginlabel.setToolTip(
_("Distance by which to avoid\n"
"the edges of the polygon to\n"
@ -185,7 +230,7 @@ class ToolPaint(FlatCAMTool, Gerber):
grid3.addWidget(self.paintmargin_entry, 2, 1)
# Method
methodlabel = QtWidgets.QLabel(_('Method:'))
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
methodlabel.setToolTip(
_("Algorithm for non-copper clearing:<BR>"
"<B>Standard</B>: Fixed step inwards.<BR>"
@ -201,7 +246,7 @@ class ToolPaint(FlatCAMTool, Gerber):
grid3.addWidget(self.paintmethod_combo, 3, 1)
# Connect lines
pathconnectlabel = QtWidgets.QLabel(_("Connect:"))
pathconnectlabel = QtWidgets.QLabel('%s:' % _("Connect"))
pathconnectlabel.setToolTip(
_("Draw lines between resulting\n"
"segments to minimize tool lifts.")
@ -210,7 +255,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.pathconnect_cb = FCCheckBox()
grid3.addWidget(self.pathconnect_cb, 4, 1)
contourlabel = QtWidgets.QLabel(_("Contour:"))
contourlabel = QtWidgets.QLabel('%s:' % _("Contour"))
contourlabel.setToolTip(
_("Cut around the perimeter of the polygon\n"
"to trim rough edges.")
@ -219,7 +264,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.paintcontour_cb = FCCheckBox()
grid3.addWidget(self.paintcontour_cb, 5, 1)
restlabel = QtWidgets.QLabel(_("Rest M.:"))
restlabel = QtWidgets.QLabel('%s:' % _("Rest M."))
restlabel.setToolTip(
_("If checked, use 'rest machining'.\n"
"Basically it will clear copper outside PCB features,\n"
@ -234,30 +279,70 @@ class ToolPaint(FlatCAMTool, Gerber):
grid3.addWidget(self.rest_cb, 6, 1)
# Polygon selection
selectlabel = QtWidgets.QLabel(_('Selection:'))
selectlabel = QtWidgets.QLabel('%s:' % _('Selection'))
selectlabel.setToolTip(
_("How to select the polygons to paint.<BR>"
"Options:<BR>"
"- <B>Single</B>: left mouse click on the polygon to be painted.<BR>"
"- <B>All</B>: paint all polygons.")
"- <B>Single Polygons</B>: left mouse click on the polygon to be painted.<BR>"
"- <B>Area Selection</B>: left mouse click to start selection of the area to be painted.<BR>"
"- <B>All Polygons</B>: paint all polygons.<BR>"
"- <B>Reference Object</B>: paint an area described by an external reference object.")
)
grid3.addWidget(selectlabel, 7, 0)
# grid3 = QtWidgets.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": _("Single"), "value": "single"},
{"label": _("Area"), "value": "area"},
{"label": _("All"), "value": "all"}
])
{"label": _("Single Polygon"), "value": "single"},
{"label": _("Area Selection"), "value": "area"},
{"label": _("All Polygons"), "value": "all"},
{"label": _("Reference Object"), "value": "ref"}
], orientation='vertical', stretch=False)
self.selectmethod_combo.setToolTip(
_("How to select Polygons to be painted.\n\n"
"- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
"- 'All Polygons' - the Paint will start after click.\n"
"- 'Reference Object' - will do non copper clearing within the area\n"
"specified by another object.")
)
grid3.addWidget(self.selectmethod_combo, 7, 1)
form1 = QtWidgets.QFormLayout()
self.tools_box.addLayout(form1)
self.box_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
self.box_combo_type_label.setToolTip(
_("The type of FlatCAM object to be used as paint reference.\n"
"It can be Gerber, Excellon or Geometry.")
)
self.box_combo_type = QtWidgets.QComboBox()
self.box_combo_type.addItem(_("Gerber Reference Box Object"))
self.box_combo_type.addItem(_("Excellon Reference Box Object"))
self.box_combo_type.addItem(_("Geometry Reference Box Object"))
form1.addRow(self.box_combo_type_label, self.box_combo_type)
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
self.box_combo_label.setToolTip(
_("The FlatCAM object to be used as non copper clearing reference.")
)
self.box_combo = QtWidgets.QComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
form1.addRow(self.box_combo_label, self.box_combo)
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
# GO Button
self.generate_paint_button = QtWidgets.QPushButton(_('Create Paint Geometry'))
self.generate_paint_button.setToolTip(
_("After clicking here, click inside<BR>"
"the polygon you wish to be painted if <B>Single</B> is selected.<BR>"
"If <B>All</B> is selected then the Paint will start after click.<BR>"
"A new Geometry object with the tool<BR>"
"paths will be created.")
_("- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
"- 'All Polygons' - the Paint will start after click.\n"
"- 'Reference Object' - will do non copper clearing within the area\n"
"specified by another object.")
)
self.tools_box.addWidget(self.generate_paint_button)
@ -271,6 +356,9 @@ class ToolPaint(FlatCAMTool, Gerber):
self.tooluid = 0
self.first_click = False
self.cursor_pos = None
self.mouse_is_dragging = False
self.sel_rect = []
# store here the default data for Geometry Data
self.default_data = {}
@ -316,6 +404,16 @@ class ToolPaint(FlatCAMTool, Gerber):
self.deltool_btn.clicked.connect(self.on_tool_delete)
self.generate_paint_button.clicked.connect(self.on_paint_button_click)
self.selectmethod_combo.activated_custom.connect(self.on_radio_selection)
self.order_radio.activated_custom[str].connect(self.on_order_changed)
self.rest_cb.stateChanged.connect(self.on_rest_machining_check)
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
def on_type_obj_index_changed(self, index):
obj_type = self.type_obj_combo.currentIndex()
self.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(0)
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+P', **kwargs)
@ -330,7 +428,12 @@ class ToolPaint(FlatCAMTool, Gerber):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
@ -342,7 +445,29 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.ui.notebook.setTabText(2, _("Paint Tool"))
def reset_usage(self):
self.obj_name = ""
self.paint_obj = None
self.bound_obj = None
self.first_click = False
self.cursor_pos = None
self.mouse_is_dragging = False
self.sel_rect = []
def on_radio_selection(self):
if self.selectmethod_combo.get_value() == "ref":
self.box_combo.show()
self.box_combo_label.show()
self.box_combo_type.show()
self.box_combo_type_label.show()
else:
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
if self.selectmethod_combo.get_value() == 'single':
# disable rest-machining for single polygon painting
self.rest_cb.set_value(False)
@ -367,11 +492,25 @@ class ToolPaint(FlatCAMTool, Gerber):
self.deltool_btn.setDisabled(False)
self.tools_table.setContextMenuPolicy(Qt.ActionsContextMenu)
def on_order_changed(self, order):
if order != 'no':
self.build_ui()
def on_rest_machining_check(self, state):
if state:
self.order_radio.set_value('rev')
self.order_label.setDisabled(True)
self.order_radio.setDisabled(True)
else:
self.order_label.setDisabled(False)
self.order_radio.setDisabled(False)
def set_tool_ui(self):
self.tools_frame.show()
self.reset_fields()
# ## Init the GUI interface
self.order_radio.set_value(self.app.defaults["tools_paintorder"])
self.paintmargin_entry.set_value(self.default_data["paintmargin"])
self.paintmethod_combo.set_value(self.default_data["paintmethod"])
self.selectmethod_combo.set_value(self.default_data["selectmethod"])
@ -452,7 +591,14 @@ class ToolPaint(FlatCAMTool, Gerber):
sorted_tools = []
for k, v in self.paint_tools.items():
sorted_tools.append(float('%.4f' % float(v['tooldia'])))
sorted_tools.sort()
order = self.order_radio.get_value()
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
n = len(sorted_tools)
self.tools_table.setRowCount(n)
@ -523,6 +669,11 @@ class ToolPaint(FlatCAMTool, Gerber):
# we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
self.tools_table.itemChanged.connect(self.on_tool_edit)
def on_combo_box_type(self):
obj_type = self.box_combo_type.currentIndex()
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
def on_tool_add(self, dia=None, muted=None):
try:
@ -541,7 +692,7 @@ class ToolPaint(FlatCAMTool, Gerber):
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
"use a number."))
return
if tool_dia is None:
@ -739,6 +890,10 @@ class ToolPaint(FlatCAMTool, Gerber):
self.build_ui()
def on_paint_button_click(self):
# init values for the next usage
self.reset_usage()
self.app.report_usage(_("geometry_on_paint_button"))
# self.app.call_source = 'paint'
@ -764,7 +919,7 @@ class ToolPaint(FlatCAMTool, Gerber):
contour = self.paintcontour_cb.get_value()
select_method = self.selectmethod_combo.get_value()
self.obj_name = self.object_combo.currentText()
self.obj_name = self.obj_combo.currentText()
# Get source object.
try:
@ -828,7 +983,7 @@ class ToolPaint(FlatCAMTool, Gerber):
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
# To be called after clicking on the plot.
def on_mouse_press(event):
def on_mouse_release(event):
# do paint single only for left mouse clicks
if event.button == 1:
if not self.first_click:
@ -839,8 +994,7 @@ class ToolPaint(FlatCAMTool, Gerber):
if self.app.grid_status() == True:
self.cursor_pos = self.app.geo_editor.snap(self.cursor_pos[0], self.cursor_pos[1])
else:
self.app.inform.emit(_("Done."))
self.first_click = False
self.app.inform.emit(_("Zone added. Right click to finish."))
self.app.delete_selection_shape()
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
@ -853,27 +1007,63 @@ class ToolPaint(FlatCAMTool, Gerber):
pt2 = (x1, y0)
pt3 = (x1, y1)
pt4 = (x0, y1)
sel_rect = Polygon([pt1, pt2, pt3, pt4])
self.sel_rect.append(Polygon([pt1, pt2, pt3, pt4]))
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
mod_key = 'Shift'
elif modifiers == QtCore.Qt.ControlModifier:
mod_key = 'Control'
else:
mod_key = None
if mod_key == self.app.defaults["global_mselect_key"]:
self.first_click = False
return
self.sel_rect = cascaded_union(self.sel_rect)
self.paint_poly_area(obj=self.paint_obj,
sel_obj= sel_rect,
sel_obj= self.sel_rect,
outname=o_name,
overlap=overlap,
connect=connect,
contour=contour)
self.app.plotcanvas.vis_disconnect('mouse_press', on_mouse_press)
self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
elif event.button == 2 and self.first_click is False and self.mouse_is_dragging is False:
self.first_click = False
self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.sel_rect = cascaded_union(self.sel_rect)
self.paint_poly_area(obj=self.paint_obj,
sel_obj=self.sel_rect,
outname=o_name,
overlap=overlap,
connect=connect,
contour=contour)
# called on mouse move
def on_mouse_move(event):
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
self.app.app_cursor.enabled = False
if event.button == 2:
if event.is_dragging is True:
self.mouse_is_dragging = True
else:
self.mouse_is_dragging = False
if self.app.grid_status() == True:
self.app.app_cursor.enabled = True
# Update cursor
@ -891,9 +1081,41 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_connect('mouse_press', on_mouse_press)
self.app.plotcanvas.vis_connect('mouse_release', on_mouse_release)
self.app.plotcanvas.vis_connect('mouse_move', on_mouse_move)
elif select_method == 'ref':
self.bound_obj_name = self.box_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
geo = self.bound_obj.solid_geometry
try:
if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry)
else:
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
sel_rect = env_obj.buffer(distance=0.0000001, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("ToolPaint.on_paint_button_click() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
self.paint_poly_area(obj=self.paint_obj,
sel_obj=sel_rect,
outname=o_name,
overlap=overlap,
connect=connect,
contour=contour)
def paint_poly(self, obj, inside_pt, tooldia, overlap, outname=None, connect=True, contour=True):
"""
Paints a polygon selected by clicking on its interior.
@ -1140,7 +1362,14 @@ class ToolPaint(FlatCAMTool, Gerber):
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True)
order = self.order_radio.get_value()
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
try:
a, b, c, d = obj.bounds()
@ -1426,10 +1655,22 @@ class ToolPaint(FlatCAMTool, Gerber):
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True)
order = self.order_radio.get_value()
if order == 'fwd':
sorted_tools.sort(reverse=False)
elif order == 'rev':
sorted_tools.sort(reverse=True)
else:
pass
geo_to_paint = []
for poly in obj.solid_geometry:
if not isinstance(obj.solid_geometry, list):
target_geo = [obj.solid_geometry]
else:
target_geo = obj.solid_geometry
for poly in target_geo:
new_pol = poly.intersection(sel_obj)
geo_to_paint.append(new_pol)
@ -1674,4 +1915,4 @@ class ToolPaint(FlatCAMTool, Gerber):
return bounds_rec(geometry)
def reset_fields(self):
self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

View File

@ -53,7 +53,7 @@ class Panelize(FlatCAMTool):
self.type_obj_combo.setItemIcon(1, QtGui.QIcon("share/drill16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel(_("Object Type:"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be panelized\n"
"It can be of type: Gerber, Excellon or Geometry.\n"
@ -68,7 +68,7 @@ class Panelize(FlatCAMTool):
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel(_("Object:"))
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(
_("Object to be panelized. This means that it will\n"
"be duplicated in an array of rows and columns.")
@ -83,7 +83,7 @@ class Panelize(FlatCAMTool):
# Type of box Panel object
self.reference_radio = RadioSet([{'label': _('Object'), 'value': 'object'},
{'label': _('Bounding Box'), 'value': 'bbox'}])
self.box_label = QtWidgets.QLabel(_("<b>Penelization Reference:</b>"))
self.box_label = QtWidgets.QLabel("<b>%s:</b>" % _("Penelization Reference"))
self.box_label.setToolTip(
_("Choose the reference for panelization:\n"
"- Object = the bounding box of a different object\n"
@ -108,7 +108,7 @@ class Panelize(FlatCAMTool):
self.type_box_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
self.type_box_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
self.type_box_combo_label = QtWidgets.QLabel(_("Box Type:"))
self.type_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Type"))
self.type_box_combo_label.setToolTip(
_("Specify the type of object to be used as an container for\n"
"panelization. It can be: Gerber or Geometry type.\n"
@ -123,7 +123,7 @@ class Panelize(FlatCAMTool):
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
self.box_combo_label = QtWidgets.QLabel(_("Box Object:"))
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
self.box_combo_label.setToolTip(
_("The actual object that is used a container for the\n "
"selected object that is to be panelized.")
@ -131,7 +131,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.box_combo_label, self.box_combo)
form_layout.addRow(QtWidgets.QLabel(""))
panel_data_label = QtWidgets.QLabel(_("<b>Panel Data:</b>"))
panel_data_label = QtWidgets.QLabel("<b>%s:</b>" % _("Panel Data"))
panel_data_label.setToolTip(
_("This informations will shape the resulting panel.\n"
"The number of rows and columns will set how many\n"
@ -144,7 +144,7 @@ class Panelize(FlatCAMTool):
# Spacing Columns
self.spacing_columns = FCEntry()
self.spacing_columns_label = QtWidgets.QLabel(_("Spacing cols:"))
self.spacing_columns_label = QtWidgets.QLabel('%s:' % _("Spacing cols"))
self.spacing_columns_label.setToolTip(
_("Spacing between columns of the desired panel.\n"
"In current units.")
@ -153,7 +153,7 @@ class Panelize(FlatCAMTool):
# Spacing Rows
self.spacing_rows = FCEntry()
self.spacing_rows_label = QtWidgets.QLabel(_("Spacing rows:"))
self.spacing_rows_label = QtWidgets.QLabel('%s:' % _("Spacing rows"))
self.spacing_rows_label.setToolTip(
_("Spacing between rows of the desired panel.\n"
"In current units.")
@ -162,7 +162,7 @@ class Panelize(FlatCAMTool):
# Columns
self.columns = FCEntry()
self.columns_label = QtWidgets.QLabel(_("Columns:"))
self.columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
self.columns_label.setToolTip(
_("Number of columns of the desired panel")
)
@ -170,7 +170,7 @@ class Panelize(FlatCAMTool):
# Rows
self.rows = FCEntry()
self.rows_label = QtWidgets.QLabel(_("Rows:"))
self.rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
self.rows_label.setToolTip(
_("Number of rows of the desired panel")
)
@ -180,7 +180,7 @@ class Panelize(FlatCAMTool):
# Type of resulting Panel object
self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
{'label': _('Geo'), 'value': 'geometry'}])
self.panel_type_label = QtWidgets.QLabel(_("<b>Panel Type:</b>"))
self.panel_type_label = QtWidgets.QLabel("<b>%s:</b>" % _("Panel Type"))
self.panel_type_label.setToolTip(
_("Choose the type of object for the panel object:\n"
"- Geometry\n"
@ -190,7 +190,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.panel_type_radio)
# Constrains
self.constrain_cb = FCCheckBox(_("Constrain panel within:"))
self.constrain_cb = FCCheckBox('%s:' % _("Constrain panel within"))
self.constrain_cb.setToolTip(
_("Area define by DX and DY within to constrain the panel.\n"
"DX and DY values are in current units.\n"
@ -201,7 +201,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.constrain_cb)
self.x_width_entry = FCEntry()
self.x_width_lbl = QtWidgets.QLabel(_("Width (DX):"))
self.x_width_lbl = QtWidgets.QLabel('%s:' % _("Width (DX)"))
self.x_width_lbl.setToolTip(
_("The width (DX) within which the panel must fit.\n"
"In current units.")
@ -209,7 +209,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.x_width_lbl, self.x_width_entry)
self.y_height_entry = FCEntry()
self.y_height_lbl = QtWidgets.QLabel(_("Height (DY):"))
self.y_height_lbl = QtWidgets.QLabel('%s:' % _("Height (DY)"))
self.y_height_lbl.setToolTip(
_("The height (DY)within which the panel must fit.\n"
"In current units.")
@ -259,7 +259,12 @@ class Panelize(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -48,13 +48,13 @@ class PcbWizard(FlatCAMTool):
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(""))
self.layout.addWidget(QtWidgets.QLabel(_("<b>Load files:</b>")))
self.layout.addWidget(QtWidgets.QLabel("<b>%s:</b>" % _("Load files")))
# Form Layout
form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout)
self.excellon_label = QtWidgets.QLabel(_("Excellon file:"))
self.excellon_label = QtWidgets.QLabel('%s:' % _("Excellon file"))
self.excellon_label.setToolTip(
_("Load the Excellon file.\n"
"Usually it has a .DRL extension")
@ -62,7 +62,7 @@ class PcbWizard(FlatCAMTool):
self.excellon_brn = FCButton(_("Open"))
form_layout.addRow(self.excellon_label, self.excellon_brn)
self.inf_label = QtWidgets.QLabel(_("INF file:"))
self.inf_label = QtWidgets.QLabel('%s:' % _("INF file"))
self.inf_label.setToolTip(
_("Load the INF file.")
)
@ -84,7 +84,7 @@ class PcbWizard(FlatCAMTool):
self.tools_table.setVisible(False)
self.layout.addWidget(QtWidgets.QLabel(""))
self.layout.addWidget(QtWidgets.QLabel(_("<b>Excellon format:</b>")))
self.layout.addWidget(QtWidgets.QLabel("<b>%s:</b>" % _("Excellon format")))
# Form Layout
form_layout1 = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout1)
@ -92,7 +92,7 @@ class PcbWizard(FlatCAMTool):
# Integral part of the coordinates
self.int_entry = FCSpinner()
self.int_entry.set_range(1, 10)
self.int_label = QtWidgets.QLabel(_("Int. digits:"))
self.int_label = QtWidgets.QLabel('%s:' % _("Int. digits"))
self.int_label.setToolTip(
_("The number of digits for the integral part of the coordinates.")
)
@ -101,7 +101,7 @@ class PcbWizard(FlatCAMTool):
# Fractional part of the coordinates
self.frac_entry = FCSpinner()
self.frac_entry.set_range(1, 10)
self.frac_label = QtWidgets.QLabel(_("Frac. digits:"))
self.frac_label = QtWidgets.QLabel('%s:' % _("Frac. digits"))
self.frac_label.setToolTip(
_("The number of digits for the fractional part of the coordinates.")
)
@ -111,7 +111,7 @@ class PcbWizard(FlatCAMTool):
self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'},
{'label': _('TZ'), 'value': 'TZ'},
{'label': _('No Suppression'), 'value': 'D'}])
self.zeros_label = QtWidgets.QLabel(_("Zeros supp.:"))
self.zeros_label = QtWidgets.QLabel('%s:' % _("Zeros supp."))
self.zeros_label.setToolTip(
_("The type of zeros suppression used.\n"
"Can be of type:\n"
@ -179,7 +179,12 @@ class PcbWizard(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -77,7 +77,12 @@ class Properties(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
@ -117,23 +122,24 @@ class Properties(FlatCAMTool):
font = QtGui.QFont()
font.setBold(True)
obj_type = self.addParent(parent, 'TYPE', expanded=True, color=QtGui.QColor("#000000"), font=font)
obj_name = self.addParent(parent, 'NAME', expanded=True, color=QtGui.QColor("#000000"), font=font)
dims = self.addParent(parent, 'Dimensions', expanded=True, color=QtGui.QColor("#000000"), font=font)
units = self.addParent(parent, 'Units', expanded=True, color=QtGui.QColor("#000000"), font=font)
obj_type = self.addParent(parent, _('TYPE'), expanded=True, color=QtGui.QColor("#000000"), font=font)
obj_name = self.addParent(parent, _('NAME'), expanded=True, color=QtGui.QColor("#000000"), font=font)
dims = self.addParent(parent, _('Dimensions'), expanded=True, color=QtGui.QColor("#000000"), font=font)
units = self.addParent(parent, _('Units'), expanded=True, color=QtGui.QColor("#000000"), font=font)
options = self.addParent(parent, 'Options', color=QtGui.QColor("#000000"), font=font)
options = self.addParent(parent, _('Options'), color=QtGui.QColor("#000000"), font=font)
if obj.kind.lower() == 'gerber':
apertures = self.addParent(parent, 'Apertures', expanded=True, color=QtGui.QColor("#000000"), font=font)
apertures = self.addParent(parent, _('Apertures'), expanded=True, color=QtGui.QColor("#000000"), font=font)
else:
tools = self.addParent(parent, 'Tools', expanded=True, color=QtGui.QColor("#000000"), font=font)
tools = self.addParent(parent, _('Tools'), expanded=True, color=QtGui.QColor("#000000"), font=font)
separator = self.addParent(parent, '')
self.addChild(obj_type, ['Object Type:', ('%s' % (obj.kind.capitalize()))], True)
self.addChild(obj_type, ['%s:' % _('Object Type'), ('%s' % (obj.kind.capitalize()))], True)
try:
self.addChild(obj_type,
['Geo Type:', ('%s' % ({False: "Single-Geo", True: "Multi-Geo"}[obj.multigeo]))],
['%s:' % _('Geo Type'),
('%s' % ({False: _("Single-Geo"), True: _("Multi-Geo")}[obj.multigeo]))],
True)
except Exception as e:
log.debug("Properties.addItems() --> %s" % str(e))
@ -150,22 +156,44 @@ class Properties(FlatCAMTool):
length = abs(xmax - xmin)
width = abs(ymax - ymin)
self.addChild(dims, ['Length:', '%.4f %s' % (
self.addChild(dims, ['%s:' % _('Length'), '%.4f %s' % (
length, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
self.addChild(dims, ['Width:', '%.4f %s' % (
self.addChild(dims, ['%s:' % _('Width'), '%.4f %s' % (
width, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
# calculate and add box area
if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
area = (length * width) / 100
self.addChild(dims, ['Box Area:', '%.4f %s' % (area, 'cm2')], True)
self.addChild(dims, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'cm2')], True)
else:
area = length * width
self.addChild(dims, ['Box Area:', '%.4f %s' % (area, 'in2')], True)
self.addChild(dims, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'in2')], True)
# calculate and add convex hull area
geo = obj.solid_geometry
if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
else:
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
area_chull = env_obj.area
if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
area_chull = area_chull / 100
self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'cm2')], True)
else:
area_chull = area_chull
self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'in2')], True)
self.addChild(units,
['FlatCAM units:',
{
'in': 'Inch',
'mm': 'Metric'
'in': _('Inch'),
'mm': _('Metric')
}
[str(self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())]
],
@ -216,7 +244,7 @@ class Properties(FlatCAMTool):
geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
for k, v in value.items():
if k == 'solid_geometry':
printed_value = 'Present' if v else 'None'
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [str(k), printed_value], True)
elif k == 'data':
tool_data = self.addParent(geo_tool, str(k).capitalize(),
@ -230,13 +258,13 @@ class Properties(FlatCAMTool):
geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
for k, v in value.items():
if k == 'solid_geometry':
printed_value = 'Present' if v else 'None'
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [str(k), printed_value], True)
elif k == 'gcode':
printed_value = 'Present' if v != '' else 'None'
printed_value = _('Present') if v != '' else _('None')
self.addChild(geo_tool, [str(k), printed_value], True)
elif k == 'gcode_parsed':
printed_value = 'Present' if v else 'None'
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [str(k), printed_value], True)
elif k == 'data':
tool_data = self.addParent(geo_tool, str(k).capitalize(),

View File

@ -139,7 +139,7 @@ class SolderPaste(FlatCAMTool):
grid0_1 = QtWidgets.QGridLayout()
self.layout.addLayout(grid0_1)
step1_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 1:'))
step1_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 1'))
step1_lbl.setToolTip(
_("First step is to select a number of nozzle tools for usage\n"
"and then optionally modify the GCode parameters bellow.")
@ -163,7 +163,7 @@ class SolderPaste(FlatCAMTool):
# Z dispense start
self.z_start_entry = FCEntry()
self.z_start_label = QtWidgets.QLabel(_("Z Dispense Start:"))
self.z_start_label = QtWidgets.QLabel('%s:' % _("Z Dispense Start"))
self.z_start_label.setToolTip(
_("The height (Z) when solder paste dispensing starts.")
)
@ -171,7 +171,7 @@ class SolderPaste(FlatCAMTool):
# Z dispense
self.z_dispense_entry = FCEntry()
self.z_dispense_label = QtWidgets.QLabel(_("Z Dispense:"))
self.z_dispense_label = QtWidgets.QLabel('%s:' % _("Z Dispense"))
self.z_dispense_label.setToolTip(
_("The height (Z) when doing solder paste dispensing.")
)
@ -179,7 +179,7 @@ class SolderPaste(FlatCAMTool):
# Z dispense stop
self.z_stop_entry = FCEntry()
self.z_stop_label = QtWidgets.QLabel(_("Z Dispense Stop:"))
self.z_stop_label = QtWidgets.QLabel('%s:' % _("Z Dispense Stop"))
self.z_stop_label.setToolTip(
_("The height (Z) when solder paste dispensing stops.")
)
@ -187,7 +187,7 @@ class SolderPaste(FlatCAMTool):
# Z travel
self.z_travel_entry = FCEntry()
self.z_travel_label = QtWidgets.QLabel(_("Z Travel:"))
self.z_travel_label = QtWidgets.QLabel('%s:' % _("Z Travel"))
self.z_travel_label.setToolTip(
_("The height (Z) for travel between pads\n"
"(without dispensing solder paste).")
@ -196,7 +196,7 @@ class SolderPaste(FlatCAMTool):
# Z toolchange location
self.z_toolchange_entry = FCEntry()
self.z_toolchange_label = QtWidgets.QLabel(_("Z Toolchange:"))
self.z_toolchange_label = QtWidgets.QLabel('%s:' % _("Z Toolchange"))
self.z_toolchange_label.setToolTip(
_("The height (Z) for tool (nozzle) change.")
)
@ -204,7 +204,7 @@ class SolderPaste(FlatCAMTool):
# X,Y Toolchange location
self.xy_toolchange_entry = FCEntry()
self.xy_toolchange_label = QtWidgets.QLabel(_("XY Toolchange:"))
self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y"))
self.xy_toolchange_label.setToolTip(
_("The X,Y location for tool (nozzle) change.\n"
"The format is (x, y) where x and y are real numbers.")
@ -213,7 +213,7 @@ class SolderPaste(FlatCAMTool):
# Feedrate X-Y
self.frxy_entry = FCEntry()
self.frxy_label = QtWidgets.QLabel(_("Feedrate X-Y:"))
self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
self.frxy_label.setToolTip(
_("Feedrate (speed) while moving on the X-Y plane.")
)
@ -221,7 +221,7 @@ class SolderPaste(FlatCAMTool):
# Feedrate Z
self.frz_entry = FCEntry()
self.frz_label = QtWidgets.QLabel(_("Feedrate Z:"))
self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
self.frz_label.setToolTip(
_("Feedrate (speed) while moving vertically\n"
"(on Z plane).")
@ -230,7 +230,7 @@ class SolderPaste(FlatCAMTool):
# Feedrate Z Dispense
self.frz_dispense_entry = FCEntry()
self.frz_dispense_label = QtWidgets.QLabel(_("Feedrate Z Dispense:"))
self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
self.frz_dispense_label.setToolTip(
_("Feedrate (speed) while moving up vertically\n"
" to Dispense position (on Z plane).")
@ -239,7 +239,7 @@ class SolderPaste(FlatCAMTool):
# Spindle Speed Forward
self.speedfwd_entry = FCEntry()
self.speedfwd_label = QtWidgets.QLabel(_("Spindle Speed FWD:"))
self.speedfwd_label = QtWidgets.QLabel('%s:' % _("Spindle Speed FWD"))
self.speedfwd_label.setToolTip(
_("The dispenser speed while pushing solder paste\n"
"through the dispenser nozzle.")
@ -248,7 +248,7 @@ class SolderPaste(FlatCAMTool):
# Dwell Forward
self.dwellfwd_entry = FCEntry()
self.dwellfwd_label = QtWidgets.QLabel(_("Dwell FWD:"))
self.dwellfwd_label = QtWidgets.QLabel('%s:' % _("Dwell FWD"))
self.dwellfwd_label.setToolTip(
_("Pause after solder dispensing.")
)
@ -256,7 +256,7 @@ class SolderPaste(FlatCAMTool):
# Spindle Speed Reverse
self.speedrev_entry = FCEntry()
self.speedrev_label = QtWidgets.QLabel(_("Spindle Speed REV:"))
self.speedrev_label = QtWidgets.QLabel('%s:' % _("Spindle Speed REV"))
self.speedrev_label.setToolTip(
_("The dispenser speed while retracting solder paste\n"
"through the dispenser nozzle.")
@ -265,7 +265,7 @@ class SolderPaste(FlatCAMTool):
# Dwell Reverse
self.dwellrev_entry = FCEntry()
self.dwellrev_label = QtWidgets.QLabel(_("Dwell REV:"))
self.dwellrev_label = QtWidgets.QLabel('%s:' % _("Dwell REV"))
self.dwellrev_label.setToolTip(
_("Pause after solder paste dispenser retracted,\n"
"to allow pressure equilibrium.")
@ -273,7 +273,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.dwellrev_label, self.dwellrev_entry)
# Postprocessors
pp_label = QtWidgets.QLabel(_('PostProcessors:'))
pp_label = QtWidgets.QLabel('%s:' % _('PostProcessor'))
pp_label.setToolTip(
_("Files that control the GCode generation.")
)
@ -303,7 +303,7 @@ class SolderPaste(FlatCAMTool):
grid2 = QtWidgets.QGridLayout()
self.generation_box.addLayout(grid2)
step2_lbl = QtWidgets.QLabel("<b>%s</b>" % _('STEP 2:'))
step2_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 2'))
step2_lbl.setToolTip(
_("Second step is to create a solder paste dispensing\n"
"geometry out of an Solder Paste Mask Gerber file.")
@ -321,7 +321,7 @@ class SolderPaste(FlatCAMTool):
self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.geo_obj_combo.setCurrentIndex(1)
self.geo_object_label = QtWidgets.QLabel(_("Geo Result:"))
self.geo_object_label = QtWidgets.QLabel('%s:' % _("Geo Result"))
self.geo_object_label.setToolTip(
_("Geometry Solder Paste object.\n"
"The name of the object has to end in:\n"
@ -332,7 +332,7 @@ class SolderPaste(FlatCAMTool):
grid3 = QtWidgets.QGridLayout()
self.generation_box.addLayout(grid3)
step3_lbl = QtWidgets.QLabel("<b>%s</b>" % _('STEP 3:'))
step3_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 3'))
step3_lbl.setToolTip(
_("Third step is to select a solder paste dispensing geometry,\n"
"and then generate a CNCJob object.\n\n"
@ -354,7 +354,7 @@ class SolderPaste(FlatCAMTool):
self.cnc_obj_combo.setRootModelIndex(self.app.collection.index(3, 0, QtCore.QModelIndex()))
self.cnc_obj_combo.setCurrentIndex(1)
self.cnc_object_label = QtWidgets.QLabel(_("CNC Result:"))
self.cnc_object_label = QtWidgets.QLabel('%s:' % _("CNC Result"))
self.cnc_object_label.setToolTip(
_("CNCJob Solder paste object.\n"
"In order to enable the GCode save section,\n"
@ -378,7 +378,7 @@ class SolderPaste(FlatCAMTool):
"on PCB pads, to a file.")
)
step4_lbl = QtWidgets.QLabel("<b>%s</b>" % _('STEP 4:'))
step4_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 4'))
step4_lbl.setToolTip(
_("Fourth step (and last) is to select a CNCJob made from \n"
"a solder paste dispensing geometry, and then view/save it's GCode.")
@ -436,7 +436,12 @@ class SolderPaste(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

View File

@ -23,6 +23,8 @@ if '_' not in builtins.__dict__:
class ToolSub(FlatCAMTool):
job_finished = QtCore.pyqtSignal(bool)
toolName = _("Substract Tool")
def __init__(self, app):
@ -52,7 +54,7 @@ class ToolSub(FlatCAMTool):
form_layout = QtWidgets.QFormLayout()
self.tools_box.addLayout(form_layout)
self.gerber_title = QtWidgets.QLabel(_("<b>Gerber Objects</b>"))
self.gerber_title = QtWidgets.QLabel("<b>%s</b>" % _("Gerber Objects"))
form_layout.addRow(self.gerber_title)
# Target Gerber Object
@ -61,7 +63,7 @@ class ToolSub(FlatCAMTool):
self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.target_gerber_combo.setCurrentIndex(1)
self.target_gerber_label = QtWidgets.QLabel(_("Target:"))
self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_gerber_label.setToolTip(
_("Gerber object from which to substract\n"
"the substractor Gerber object.")
@ -75,7 +77,7 @@ class ToolSub(FlatCAMTool):
self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sub_gerber_combo.setCurrentIndex(1)
self.sub_gerber_label = QtWidgets.QLabel(_("Substractor:"))
self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Substractor"))
self.sub_gerber_label.setToolTip(
_("Gerber object that will be substracted\n"
"from the target Gerber object.")
@ -98,7 +100,7 @@ class ToolSub(FlatCAMTool):
form_geo_layout = QtWidgets.QFormLayout()
self.tools_box.addLayout(form_geo_layout)
self.geo_title = QtWidgets.QLabel(_("<b>Geometry Objects</b>"))
self.geo_title = QtWidgets.QLabel("<b>%s</b>" % _("Geometry Objects"))
form_geo_layout.addRow(self.geo_title)
# Target Geometry Object
@ -107,7 +109,7 @@ class ToolSub(FlatCAMTool):
self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.target_geo_combo.setCurrentIndex(1)
self.target_geo_label = QtWidgets.QLabel(_("Target:"))
self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_geo_label.setToolTip(
_("Geometry object from which to substract\n"
"the substractor Geometry object.")
@ -121,7 +123,7 @@ class ToolSub(FlatCAMTool):
self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.sub_geo_combo.setCurrentIndex(1)
self.sub_geo_label = QtWidgets.QLabel(_("Substractor:"))
self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Substractor"))
self.sub_geo_label.setToolTip(
_("Geometry object that will be substracted\n"
"from the target Geometry object.")
@ -188,6 +190,7 @@ class ToolSub(FlatCAMTool):
except (TypeError, AttributeError):
pass
self.intersect_geo_btn.clicked.connect(self.on_geo_intersection_click)
self.job_finished.connect(self.on_job_finished)
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+W', **kwargs)
@ -202,7 +205,12 @@ class ToolSub(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
@ -286,7 +294,7 @@ class ToolSub(FlatCAMTool):
for apid in self.target_grb_obj.apertures:
self.promises.append(apid)
# start the QTimer to check for promises with 1 second period check
# start the QTimer to check for promises with 0.5 second period check
self.periodic_check(500, reset=True)
for apid in self.target_grb_obj.apertures:
@ -413,7 +421,10 @@ class ToolSub(FlatCAMTool):
# cleanup
self.new_apertures.clear()
self.new_solid_geometry[:] = []
self.sub_union[:] = []
try:
self.sub_union[:] = []
except TypeError:
self.sub_union = []
def on_geo_intersection_click(self):
# reset previous values
@ -455,14 +466,14 @@ class ToolSub(FlatCAMTool):
return
# create the target_options obj
self.target_options = {}
for opt in self.target_geo_obj.options:
if opt != 'name':
self.target_options[opt] = deepcopy(self.target_geo_obj.options[opt])
# self.target_options = dict()
# for k, v in self.target_geo_obj.options.items():
# if k != 'name':
# self.target_options[k] = v
# crate the new_tools dict structure
for tool in self.target_geo_obj.tools:
self.new_tools[tool] = {}
self.new_tools[tool] = dict()
for key in self.target_geo_obj.tools[tool]:
if key == 'solid_geometry':
self.new_tools[tool][key] = []
@ -514,16 +525,14 @@ class ToolSub(FlatCAMTool):
if isinstance(geo_elem, Polygon):
for ring in self.poly2rings(geo_elem):
new_geo = ring.difference(self.sub_union)
if new_geo:
if not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geo and not new_geo.is_empty:
new_geometry.append(new_geo)
elif isinstance(geo_elem, MultiPolygon):
for poly in geo_elem:
for ring in self.poly2rings(poly):
new_geo = ring.difference(self.sub_union)
if new_geo:
if not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geo and not new_geo.is_empty:
new_geometry.append(new_geo)
elif isinstance(geo_elem, LineString):
new_geo = geo_elem.difference(self.sub_union)
if new_geo:
@ -532,9 +541,8 @@ class ToolSub(FlatCAMTool):
elif isinstance(geo_elem, MultiLineString):
for line_elem in geo_elem:
new_geo = line_elem.difference(self.sub_union)
if new_geo:
if not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geo and not new_geo.is_empty:
new_geometry.append(new_geo)
except TypeError:
if isinstance(geo, Polygon):
for ring in self.poly2rings(geo):
@ -544,15 +552,13 @@ class ToolSub(FlatCAMTool):
new_geometry.append(new_geo)
elif isinstance(geo, LineString):
new_geo = geo.difference(self.sub_union)
if new_geo:
if not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geo and not new_geo.is_empty:
new_geometry.append(new_geo)
elif isinstance(geo, MultiLineString):
for line_elem in geo:
new_geo = line_elem.difference(self.sub_union)
if new_geo:
if not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geo and not new_geo.is_empty:
new_geometry.append(new_geo)
if new_geometry:
if tool == "single":
@ -575,10 +581,14 @@ class ToolSub(FlatCAMTool):
log.debug("Promise fulfilled: %s" % str(tool))
def new_geo_object(self, outname):
geo_name = outname
def obj_init(geo_obj, app_obj):
geo_obj.options = deepcopy(self.target_options)
geo_obj.options['name'] = outname
# geo_obj.options = self.target_options
# create the target_options obj
for k, v in self.target_geo_obj.options.items():
geo_obj.options[k] = v
geo_obj.options['name'] = geo_name
if self.target_geo_obj.multigeo:
geo_obj.tools = deepcopy(self.new_tools)
@ -593,6 +603,7 @@ class ToolSub(FlatCAMTool):
geo_obj.tools[tool]['solid_geometry'] = deepcopy(self.new_solid_geometry)
except Exception as e:
log.debug("ToolSub.new_geo_object() --> %s" % str(e))
geo_obj.multigeo = False
with self.app.proc_container.new(_("Generating new object ...")):
ret = self.app.new_object('geometry', outname, obj_init, autoselected=False)
@ -607,7 +618,7 @@ class ToolSub(FlatCAMTool):
# cleanup
self.new_tools.clear()
self.new_solid_geometry[:] = []
self.sub_union[:] = []
self.sub_union = []
def periodic_check(self, check_period, reset=False):
"""
@ -645,27 +656,39 @@ class ToolSub(FlatCAMTool):
try:
if not self.promises:
self.check_thread.stop()
if self.sub_type == "gerber":
outname = self.target_gerber_combo.currentText() + '_sub'
# intersection jobs finished, start the creation of solid_geometry
self.app.worker_task.emit({'fcn': self.new_gerber_object,
'params': [outname]})
else:
outname = self.target_geo_combo.currentText() + '_sub'
# intersection jobs finished, start the creation of solid_geometry
self.app.worker_task.emit({'fcn': self.new_geo_object,
'params': [outname]})
self.job_finished.emit(True)
# reset the type of substraction for next time
self.sub_type = None
log.debug("ToolSub --> Periodic check finished.")
except Exception as e:
self.job_finished.emit(False)
log.debug("ToolSub().periodic_check_handler() --> %s" % str(e))
traceback.print_exc()
def on_job_finished(self, succcess):
"""
:param succcess: boolean, this parameter signal if all the apertures were processed
:return: None
"""
if succcess is True:
if self.sub_type == "gerber":
outname = self.target_gerber_combo.currentText() + '_sub'
# intersection jobs finished, start the creation of solid_geometry
self.app.worker_task.emit({'fcn': self.new_gerber_object,
'params': [outname]})
else:
outname = self.target_geo_combo.currentText() + '_sub'
# intersection jobs finished, start the creation of solid_geometry
self.app.worker_task.emit({'fcn': self.new_geo_object,
'params': [outname]})
else:
self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
def reset_fields(self):
self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

View File

@ -65,7 +65,7 @@ class ToolTransform(FlatCAMTool):
self.transform_lay.addLayout(form_layout)
form_child = QtWidgets.QHBoxLayout()
self.rotate_label = QtWidgets.QLabel(_("Angle:"))
self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
self.rotate_label.setToolTip(
_("Angle for Rotation action, in degrees.\n"
"Float number between -360 and 359.\n"
@ -104,7 +104,7 @@ class ToolTransform(FlatCAMTool):
form1_child_1 = QtWidgets.QHBoxLayout()
form1_child_2 = QtWidgets.QHBoxLayout()
self.skewx_label = QtWidgets.QLabel(_("Angle X:"))
self.skewx_label = QtWidgets.QLabel('%s:' % _("Skew_X angle"))
self.skewx_label.setToolTip(
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
@ -122,7 +122,7 @@ class ToolTransform(FlatCAMTool):
"the bounding box for all selected objects."))
self.skewx_button.setMinimumWidth(90)
self.skewy_label = QtWidgets.QLabel(_("Angle Y:"))
self.skewy_label = QtWidgets.QLabel('%s:' % _("Skew_Y angle"))
self.skewy_label.setToolTip(
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
@ -161,9 +161,9 @@ class ToolTransform(FlatCAMTool):
form2_child_1 = QtWidgets.QHBoxLayout()
form2_child_2 = QtWidgets.QHBoxLayout()
self.scalex_label = QtWidgets.QLabel(_("Factor X:"))
self.scalex_label = QtWidgets.QLabel('%s:' % _("Scale_X factor"))
self.scalex_label.setToolTip(
_("Factor for Scale action over X axis.")
_("Factor for scaling on X axis.")
)
self.scalex_label.setMinimumWidth(70)
self.scalex_entry = FCEntry()
@ -178,9 +178,9 @@ class ToolTransform(FlatCAMTool):
"the Scale reference checkbox state."))
self.scalex_button.setMinimumWidth(90)
self.scaley_label = QtWidgets.QLabel(_("Factor Y:"))
self.scaley_label = QtWidgets.QLabel('%s:' % _("Scale_Y factor"))
self.scaley_label.setToolTip(
_("Factor for Scale action over Y axis.")
_("Factor for scaling on Y axis.")
)
self.scaley_label.setMinimumWidth(70)
self.scaley_entry = FCEntry()
@ -200,12 +200,13 @@ class ToolTransform(FlatCAMTool):
self.scale_link_cb.setText(_("Link"))
self.scale_link_cb.setToolTip(
_("Scale the selected object(s)\n"
"using the Scale Factor X for both axis."))
"using the Scale_X factor for both axis.")
)
self.scale_link_cb.setMinimumWidth(70)
self.scale_zero_ref_cb = FCCheckBox()
self.scale_zero_ref_cb.set_value(True)
self.scale_zero_ref_cb.setText(_("Scale Reference"))
self.scale_zero_ref_cb.setText('%s' % _("Scale Reference"))
self.scale_zero_ref_cb.setToolTip(
_("Scale the selected object(s)\n"
"using the origin reference when checked,\n"
@ -235,9 +236,9 @@ class ToolTransform(FlatCAMTool):
form3_child_1 = QtWidgets.QHBoxLayout()
form3_child_2 = QtWidgets.QHBoxLayout()
self.offx_label = QtWidgets.QLabel(_("Value X:"))
self.offx_label = QtWidgets.QLabel('%s:' % _("Offset_X val"))
self.offx_label.setToolTip(
_("Value for Offset action on X axis.")
_("Distance to offset on X axis. In current units.")
)
self.offx_label.setMinimumWidth(70)
self.offx_entry = FCEntry()
@ -252,9 +253,9 @@ class ToolTransform(FlatCAMTool):
"the bounding box for all selected objects.\n"))
self.offx_button.setMinimumWidth(90)
self.offy_label = QtWidgets.QLabel(_("Value Y:"))
self.offy_label = QtWidgets.QLabel('%s:' % _("Offset_Y val"))
self.offy_label.setToolTip(
_("Value for Offset action on Y axis.")
_("Distance to offset on Y axis. In current units.")
)
self.offy_label.setMinimumWidth(70)
self.offy_entry = FCEntry()
@ -309,7 +310,7 @@ class ToolTransform(FlatCAMTool):
self.flip_ref_cb = FCCheckBox()
self.flip_ref_cb.set_value(True)
self.flip_ref_cb.setText(_("Ref Pt"))
self.flip_ref_cb.setText('%s' % _("Mirror Reference"))
self.flip_ref_cb.setToolTip(
_("Flip the selected object(s)\n"
"around the point in Point Entry Field.\n"
@ -322,7 +323,7 @@ class ToolTransform(FlatCAMTool):
"Point Entry field and click Flip on X(Y)"))
self.flip_ref_cb.setMinimumWidth(70)
self.flip_ref_label = QtWidgets.QLabel(_("Point:"))
self.flip_ref_label = QtWidgets.QLabel('%s:' % _(" Mirror Ref. Point"))
self.flip_ref_label.setToolTip(
_("Coordinates in format (x, y) used as reference for mirroring.\n"
"The 'x' in (x, y) will be used when using Flip on X and\n"
@ -384,7 +385,12 @@ class ToolTransform(FlatCAMTool):
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
self.app.ui.splitter.setSizes([0, 1])
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -57,7 +57,7 @@ class TclCommandFollow(TclCommandSignaled):
del args['name']
try:
obj.follow(**args)
obj.follow_geo(**args)
except Exception as e:
return "Operation failed: %s" % str(e)