commit
e7a32c5c90
320
FlatCAMApp.py
320
FlatCAMApp.py
|
@ -94,8 +94,8 @@ class App(QtCore.QObject):
|
|||
log.addHandler(handler)
|
||||
|
||||
# Version
|
||||
version = 8.914
|
||||
version_date = "2019/04/23"
|
||||
version = 8.915
|
||||
version_date = "2019/05/1"
|
||||
beta = True
|
||||
|
||||
# current date now
|
||||
|
@ -189,6 +189,8 @@ class App(QtCore.QObject):
|
|||
|
||||
App.log.info("FlatCAM Starting...")
|
||||
|
||||
self.main_thread = QtWidgets.QApplication.instance().thread()
|
||||
|
||||
###################
|
||||
### OS-specific ###
|
||||
###################
|
||||
|
@ -340,10 +342,13 @@ class App(QtCore.QObject):
|
|||
"global_draw_color": self.ui.general_defaults_form.general_gui_group.draw_color_entry,
|
||||
"global_sel_draw_color": self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry,
|
||||
|
||||
"global_proj_item_color": self.ui.general_defaults_form.general_gui_group.proj_color_entry,
|
||||
"global_proj_item_dis_color": self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry,
|
||||
|
||||
# General GUI Settings
|
||||
"global_layout": self.ui.general_defaults_form.general_gui_set_group.layout_combo,
|
||||
"global_hover": self.ui.general_defaults_form.general_gui_set_group.hover_cb,
|
||||
|
||||
"global_selection_shape": self.ui.general_defaults_form.general_gui_set_group.selection_cb,
|
||||
# Gerber General
|
||||
"gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb,
|
||||
"gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb,
|
||||
|
@ -612,6 +617,8 @@ class App(QtCore.QObject):
|
|||
"global_alt_sel_line": '#006E20BF',
|
||||
"global_draw_color": '#FF0000',
|
||||
"global_sel_draw_color": '#0000FF',
|
||||
"global_proj_item_color": '#000000',
|
||||
"global_proj_item_dis_color": '#b7b7cb',
|
||||
|
||||
"global_toolbar_view": 511,
|
||||
|
||||
|
@ -647,6 +654,7 @@ class App(QtCore.QObject):
|
|||
|
||||
# General GUI Settings
|
||||
"global_hover": True,
|
||||
"global_selection_shape": True,
|
||||
"global_layout": "compact",
|
||||
# Gerber General
|
||||
"gerber_plot": True,
|
||||
|
@ -1162,7 +1170,8 @@ class App(QtCore.QObject):
|
|||
"background-color:%s" % str(self.defaults['global_sel_line'])[:7])
|
||||
|
||||
# Init Right-Left Selection colors
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(self.defaults['global_alt_sel_fill'])
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
|
||||
self.defaults['global_alt_sel_fill'])
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_alt_sel_fill'])[:7])
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value(
|
||||
|
@ -1170,18 +1179,33 @@ class App(QtCore.QObject):
|
|||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(
|
||||
int(self.defaults['global_sel_fill'][7:9], 16))
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(self.defaults['global_alt_sel_line'])
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
|
||||
self.defaults['global_alt_sel_line'])
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_alt_sel_line'])[:7])
|
||||
|
||||
# Init Draw color and Selection Draw Color
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(self.defaults['global_draw_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
|
||||
self.defaults['global_draw_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_draw_color'])[:7])
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(self.defaults['global_sel_draw_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
|
||||
self.defaults['global_sel_draw_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_sel_draw_color'])[:7])
|
||||
|
||||
# Init Project Items color
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
|
||||
self.defaults['global_proj_item_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_proj_item_color'])[:7])
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
|
||||
self.defaults['global_proj_item_dis_color'])
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_proj_item_dis_color'])[:7])
|
||||
|
||||
#### End of Data ####
|
||||
|
||||
#### Plot Area ####
|
||||
|
@ -1362,29 +1386,10 @@ class App(QtCore.QObject):
|
|||
self.ui.popmenu_disable.triggered.connect(lambda: self.disable_plots(self.collection.get_selected()))
|
||||
|
||||
self.ui.popmenu_new_geo.triggered.connect(self.new_geometry_object)
|
||||
self.ui.popmenu_new_grb.triggered.connect(self.new_gerber_object)
|
||||
self.ui.popmenu_new_exc.triggered.connect(self.new_excellon_object)
|
||||
self.ui.popmenu_new_prj.triggered.connect(self.on_file_new)
|
||||
|
||||
# Geometry Editor
|
||||
self.ui.draw_line.triggered.connect(self.geo_editor.draw_tool_path)
|
||||
self.ui.draw_rect.triggered.connect(self.geo_editor.draw_tool_rectangle)
|
||||
self.ui.draw_cut.triggered.connect(self.geo_editor.cutpath)
|
||||
self.ui.draw_move.triggered.connect(self.geo_editor.on_move)
|
||||
|
||||
# Gerber Editor
|
||||
self.ui.grb_draw_pad.triggered.connect(self.grb_editor.on_pad_add)
|
||||
self.ui.grb_draw_pad_array.triggered.connect(self.grb_editor.on_pad_add_array)
|
||||
self.ui.grb_draw_track.triggered.connect(self.grb_editor.on_track_add)
|
||||
self.ui.grb_draw_region.triggered.connect(self.grb_editor.on_region_add)
|
||||
self.ui.grb_copy.triggered.connect(self.grb_editor.on_copy_button)
|
||||
self.ui.grb_delete.triggered.connect(self.grb_editor.on_delete_btn)
|
||||
self.ui.grb_move.triggered.connect(self.grb_editor.on_move_button)
|
||||
|
||||
# Excellon Editor
|
||||
self.ui.drill.triggered.connect(self.exc_editor.exc_add_drill)
|
||||
self.ui.drill_array.triggered.connect(self.exc_editor.exc_add_drill_array)
|
||||
self.ui.drill_copy.triggered.connect(self.exc_editor.exc_copy_drills)
|
||||
|
||||
self.ui.zoomfit.triggered.connect(self.on_zoom_fit)
|
||||
self.ui.clearplot.triggered.connect(self.clear_plots)
|
||||
self.ui.replot.triggered.connect(self.plot_all)
|
||||
|
@ -1418,34 +1423,64 @@ class App(QtCore.QObject):
|
|||
###############################
|
||||
|
||||
# Setting plot colors signals
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_entry.editingFinished.connect(self.on_pf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_button.clicked.connect(self.on_pf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_alpha_spinner.valueChanged.connect(self.on_pf_color_spinner)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.valueChanged.connect(self.on_pf_color_slider)
|
||||
self.ui.general_defaults_form.general_gui_group.pl_color_entry.editingFinished.connect(self.on_pl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.pl_color_button.clicked.connect(self.on_pl_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_entry.editingFinished.connect(
|
||||
self.on_pf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_button.clicked.connect(
|
||||
self.on_pf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_alpha_spinner.valueChanged.connect(
|
||||
self.on_pf_color_spinner)
|
||||
self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.valueChanged.connect(
|
||||
self.on_pf_color_slider)
|
||||
self.ui.general_defaults_form.general_gui_group.pl_color_entry.editingFinished.connect(
|
||||
self.on_pl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.pl_color_button.clicked.connect(
|
||||
self.on_pl_color_button)
|
||||
# Setting selection (left - right) colors signals
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_button.clicked.connect(self.on_sf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider)
|
||||
self.ui.general_defaults_form.general_gui_group.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sl_color_button.clicked.connect(self.on_sl_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_entry.editingFinished.connect(
|
||||
self.on_sf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_button.clicked.connect(
|
||||
self.on_sf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.valueChanged.connect(
|
||||
self.on_sf_color_spinner)
|
||||
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.valueChanged.connect(
|
||||
self.on_sf_color_slider)
|
||||
self.ui.general_defaults_form.general_gui_group.sl_color_entry.editingFinished.connect(
|
||||
self.on_sl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sl_color_button.clicked.connect(
|
||||
self.on_sl_color_button)
|
||||
# Setting selection (right - left) colors signals
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.editingFinished.connect(
|
||||
self.on_alt_sf_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.clicked.connect(
|
||||
self.on_alt_sf_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.valueChanged.connect(
|
||||
self.on_alt_sf_color_spinner)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.valueChanged.connect(
|
||||
self.on_alt_sf_color_slider)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.editingFinished.connect(
|
||||
self.on_alt_sl_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.clicked.connect(
|
||||
self.on_alt_sl_color_button)
|
||||
# Setting Editor Draw colors signals
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_button.clicked.connect(self.on_draw_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_entry.editingFinished.connect(
|
||||
self.on_draw_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.draw_color_button.clicked.connect(
|
||||
self.on_draw_color_button)
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button)
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.editingFinished.connect(
|
||||
self.on_sel_draw_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.clicked.connect(
|
||||
self.on_sel_draw_color_button)
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_entry.editingFinished.connect(
|
||||
self.on_proj_color_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_button.clicked.connect(
|
||||
self.on_proj_color_button)
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.editingFinished.connect(
|
||||
self.on_proj_color_dis_entry)
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.clicked.connect(
|
||||
self.on_proj_color_dis_button)
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified)
|
||||
self.ui.general_defaults_form.general_gui_group.workspace_cb.stateChanged.connect(self.on_workspace)
|
||||
|
@ -1772,7 +1807,7 @@ class App(QtCore.QObject):
|
|||
self.thr2 = QtCore.QThread()
|
||||
self.worker_task.emit({'fcn': self.version_check,
|
||||
'params': []})
|
||||
self.thr2.start()
|
||||
self.thr2.start(QtCore.QThread.LowPriority)
|
||||
|
||||
|
||||
####################################
|
||||
|
@ -1790,8 +1825,6 @@ class App(QtCore.QObject):
|
|||
# decide if we have a double click or single click
|
||||
self.doubleclick = False
|
||||
|
||||
# variable to store if there was motion before right mouse button click (panning)
|
||||
self.panning_action = False
|
||||
# variable to store if a command is active (then the var is not None) and which one it is
|
||||
self.command_active = None
|
||||
# variable to store the status of moving selection action
|
||||
|
@ -1977,7 +2010,15 @@ class App(QtCore.QObject):
|
|||
self.film_tool.install(icon=QtGui.QIcon('share/film16.png'))
|
||||
|
||||
self.paste_tool = SolderPaste(self)
|
||||
self.paste_tool.install(icon=QtGui.QIcon('share/solderpastebis32.png'), separator=True)
|
||||
self.paste_tool.install(icon=QtGui.QIcon('share/solderpastebis32.png'))
|
||||
|
||||
self.calculator_tool = ToolCalculator(self)
|
||||
self.calculator_tool.install(icon=QtGui.QIcon('share/calculator24.png'))
|
||||
|
||||
|
||||
self.sub_tool = ToolSub(self)
|
||||
self.sub_tool.install(icon=QtGui.QIcon('share/sub32.png'), pos=self.ui.menuedit_convert,
|
||||
before=self.ui.menuedit_convert_sg2mg)
|
||||
|
||||
self.move_tool = ToolMove(self)
|
||||
self.move_tool.install(icon=QtGui.QIcon('share/move16.png'), pos=self.ui.menuedit,
|
||||
|
@ -1995,9 +2036,6 @@ class App(QtCore.QObject):
|
|||
self.paint_tool.install(icon=QtGui.QIcon('share/paint16.png'), pos=self.ui.menutool,
|
||||
before=self.measurement_tool.menuAction, separator=True)
|
||||
|
||||
self.calculator_tool = ToolCalculator(self)
|
||||
self.calculator_tool.install(icon=QtGui.QIcon('share/calculator24.png'))
|
||||
|
||||
self.transform_tool = ToolTransform(self)
|
||||
self.transform_tool.install(icon=QtGui.QIcon('share/transform.png'), pos=self.ui.menuoptions, separator=True)
|
||||
|
||||
|
@ -2081,6 +2119,7 @@ class App(QtCore.QObject):
|
|||
self.ui.panelize_btn.triggered.connect(lambda: self.panelize_tool.run(toggle=True))
|
||||
self.ui.film_btn.triggered.connect(lambda: self.film_tool.run(toggle=True))
|
||||
self.ui.solder_btn.triggered.connect(lambda: self.paste_tool.run(toggle=True))
|
||||
self.ui.sub_btn.triggered.connect(lambda: self.sub_tool.run(toggle=True))
|
||||
|
||||
self.ui.calculators_btn.triggered.connect(lambda: self.calculator_tool.run(toggle=True))
|
||||
self.ui.transform_btn.triggered.connect(lambda: self.transform_tool.run(toggle=True))
|
||||
|
@ -2138,8 +2177,9 @@ class App(QtCore.QObject):
|
|||
# set call source to the Editor we go into
|
||||
self.call_source = 'grb_editor'
|
||||
|
||||
# make sure that we can't select another object while in Editor Mode:
|
||||
self.collection.view.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
# # make sure that we can't select another object while in Editor Mode:
|
||||
# self.collection.view.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.ui.project_frame.setDisabled(True)
|
||||
|
||||
# delete any selection shape that might be active as they are not relevant in Editor
|
||||
self.delete_selection_shape()
|
||||
|
@ -2178,6 +2218,11 @@ class App(QtCore.QObject):
|
|||
response = msgbox.clickedButton()
|
||||
|
||||
if response == bt_yes:
|
||||
# clean the Tools Tab
|
||||
self.ui.tool_scroll_area.takeWidget()
|
||||
self.ui.tool_scroll_area.setWidget(QtWidgets.QWidget())
|
||||
self.ui.notebook.setTabText(2, "Tool")
|
||||
|
||||
if isinstance(edited_obj, FlatCAMGeometry):
|
||||
obj_type = "Geometry"
|
||||
if cleanup is None:
|
||||
|
@ -2232,6 +2277,11 @@ class App(QtCore.QObject):
|
|||
|
||||
self.inform.emit(_("[selected] %s is updated, returning to App...") % obj_type)
|
||||
elif response == bt_no:
|
||||
# clean the Tools Tab
|
||||
self.ui.tool_scroll_area.takeWidget()
|
||||
self.ui.tool_scroll_area.setWidget(QtWidgets.QWidget())
|
||||
self.ui.notebook.setTabText(2, "Tool")
|
||||
|
||||
if isinstance(edited_obj, FlatCAMGeometry):
|
||||
self.geo_editor.deactivate()
|
||||
elif isinstance(edited_obj, FlatCAMGerber):
|
||||
|
@ -2268,7 +2318,8 @@ class App(QtCore.QObject):
|
|||
self.ui.plot_tab_area.protectTab(0)
|
||||
|
||||
# make sure that we reenable the selection on Project Tab after returning from Editor Mode:
|
||||
self.collection.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
# self.collection.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.ui.project_frame.setDisabled(False)
|
||||
|
||||
def get_last_folder(self):
|
||||
return self.defaults["global_last_folder"]
|
||||
|
@ -2773,7 +2824,7 @@ class App(QtCore.QObject):
|
|||
|
||||
def new_object(self, kind, name, initialize, active=True, fit=True, plot=True, autoselected=True):
|
||||
"""
|
||||
Creates a new specalized FlatCAMObj and attaches it to the application,
|
||||
Creates a new specialized FlatCAMObj and attaches it to the application,
|
||||
this is, updates the GUI accordingly, any other records and plots it.
|
||||
This method is thread-safe.
|
||||
|
||||
|
@ -2883,7 +2934,7 @@ class App(QtCore.QObject):
|
|||
FlatCAMApp.App.log.debug("Moving new object back to main thread.")
|
||||
|
||||
# Move the object to the main thread and let the app know that it is available.
|
||||
obj.moveToThread(QtWidgets.QApplication.instance().thread())
|
||||
obj.moveToThread(self.main_thread)
|
||||
self.object_created.emit(obj, obj_plot, obj_autoselected)
|
||||
|
||||
return obj
|
||||
|
@ -2911,6 +2962,14 @@ class App(QtCore.QObject):
|
|||
grb_obj.follow = False
|
||||
grb_obj.apertures = {}
|
||||
|
||||
try:
|
||||
grb_obj.options['xmin'] = 0
|
||||
grb_obj.options['ymin'] = 0
|
||||
grb_obj.options['xmax'] = 0
|
||||
grb_obj.options['ymax'] = 0
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
self.new_object('gerber', 'new_grb', initialize, plot=False)
|
||||
|
||||
def on_object_created(self, obj, plot, autoselect):
|
||||
|
@ -4024,6 +4083,50 @@ class App(QtCore.QObject):
|
|||
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(new_val_sel)
|
||||
self.defaults['global_sel_draw_color'] = new_val_sel
|
||||
|
||||
def on_proj_color_entry(self):
|
||||
self.defaults['global_proj_item_color'] = self.ui.general_defaults_form.general_gui_group \
|
||||
.proj_color_entry.get_value()
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_proj_item_color']))
|
||||
|
||||
def on_proj_color_button(self):
|
||||
current_color = QtGui.QColor(self.defaults['global_proj_item_color'])
|
||||
|
||||
c_dialog = QtWidgets.QColorDialog()
|
||||
proj_color = c_dialog.getColor(initial=current_color)
|
||||
|
||||
if proj_color.isValid() is False:
|
||||
return
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
|
||||
"background-color:%s" % str(proj_color.name()))
|
||||
|
||||
new_val_sel = str(proj_color.name())
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(new_val_sel)
|
||||
self.defaults['global_proj_item_color'] = new_val_sel
|
||||
|
||||
def on_proj_color_dis_entry(self):
|
||||
self.defaults['global_proj_item_dis_color'] = self.ui.general_defaults_form.general_gui_group \
|
||||
.proj_color_dis_entry.get_value()
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
|
||||
"background-color:%s" % str(self.defaults['global_proj_item_dis_color']))
|
||||
|
||||
def on_proj_color_dis_button(self):
|
||||
current_color = QtGui.QColor(self.defaults['global_proj_item_dis_color'])
|
||||
|
||||
c_dialog = QtWidgets.QColorDialog()
|
||||
proj_color = c_dialog.getColor(initial=current_color)
|
||||
|
||||
if proj_color.isValid() is False:
|
||||
return
|
||||
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
|
||||
"background-color:%s" % str(proj_color.name()))
|
||||
|
||||
new_val_sel = str(proj_color.name())
|
||||
self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(new_val_sel)
|
||||
self.defaults['global_proj_item_dis_color'] = new_val_sel
|
||||
|
||||
def on_deselect_all(self):
|
||||
self.collection.set_all_inactive()
|
||||
self.delete_selection_shape()
|
||||
|
@ -4481,7 +4584,7 @@ class App(QtCore.QObject):
|
|||
"""
|
||||
self.report_usage("on_jump_to()")
|
||||
|
||||
if custom_location is None:
|
||||
if not custom_location:
|
||||
dia_box = Dialog_box(title=_("Jump to ..."),
|
||||
label=_("Enter the coordinates in format X,Y:"),
|
||||
icon=QtGui.QIcon('share/jump_to16.png'))
|
||||
|
@ -4627,9 +4730,14 @@ class App(QtCore.QObject):
|
|||
if obj.tools:
|
||||
obj_init.tools = obj.tools
|
||||
|
||||
def initialize_excellon(obj, app):
|
||||
objs = self.collection.get_selected()
|
||||
FlatCAMGeometry.merge(objs, obj)
|
||||
def initialize_excellon(obj_init, app):
|
||||
# objs = self.collection.get_selected()
|
||||
# FlatCAMGeometry.merge(objs, obj)
|
||||
solid_geo = []
|
||||
for tool in obj.tools:
|
||||
for geo in obj.tools[tool]['solid_geometry']:
|
||||
solid_geo.append(geo)
|
||||
obj_init.solid_geometry = deepcopy(solid_geo)
|
||||
|
||||
for obj in self.collection.get_selected():
|
||||
|
||||
|
@ -4680,6 +4788,7 @@ class App(QtCore.QObject):
|
|||
self.collection.set_active(name)
|
||||
curr_sel_obj = self.collection.get_by_name(name)
|
||||
# create the selection box around the selected object
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(curr_sel_obj)
|
||||
|
||||
def on_preferences(self):
|
||||
|
@ -4915,6 +5024,7 @@ class App(QtCore.QObject):
|
|||
# self.plotcanvas.auto_adjust_axes()
|
||||
self.plotcanvas.vispy_canvas.update() # TODO: Need update canvas?
|
||||
self.on_zoom_fit(None)
|
||||
self.collection.update_view()
|
||||
|
||||
# TODO: Rework toolbar 'clear', 'replot' functions
|
||||
def on_toolbar_replot(self):
|
||||
|
@ -4957,9 +5067,9 @@ class App(QtCore.QObject):
|
|||
action.triggered.connect(self.set_grid)
|
||||
|
||||
self.ui.cmenu_gridmenu.addSeparator()
|
||||
grid_add = self.ui.cmenu_gridmenu.addAction(QtGui.QIcon('share/plus32.png'), "Add")
|
||||
grid_add = self.ui.cmenu_gridmenu.addAction(QtGui.QIcon('share/plus32.png'), _("Add"))
|
||||
grid_delete = self.ui.cmenu_gridmenu.addAction(QtGui.QIcon('share/delete32.png'), _("Delete"))
|
||||
grid_add.triggered.connect(self.on_grid_add)
|
||||
grid_delete = self.ui.cmenu_gridmenu.addAction(QtGui.QIcon('share/delete32.png'), "Delete")
|
||||
grid_delete.triggered.connect(self.on_grid_delete)
|
||||
|
||||
def set_grid(self):
|
||||
|
@ -4970,8 +5080,8 @@ class App(QtCore.QObject):
|
|||
## Current application units in lower Case
|
||||
units = self.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
grid_add_popup = FCInputDialog(title="New Grid ...",
|
||||
text='Enter a Grid VAlue:',
|
||||
grid_add_popup = FCInputDialog(title=_("New Grid ..."),
|
||||
text=_('Enter a Grid Value:'),
|
||||
min=0.0000, max=99.9999, decimals=4)
|
||||
grid_add_popup.setWindowIcon(QtGui.QIcon('share/plus32.png'))
|
||||
|
||||
|
@ -5126,15 +5236,13 @@ class App(QtCore.QObject):
|
|||
self.plotcanvas.vispy_canvas.native.setFocus()
|
||||
self.pos_jump = event.pos
|
||||
|
||||
if origin_click is True:
|
||||
pass
|
||||
else:
|
||||
self.ui.popMenu.mouse_is_panning = False
|
||||
|
||||
if origin_click != True:
|
||||
# if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
||||
if event.button == 2:
|
||||
self.panning_action = True
|
||||
if event.button == 2 and event.is_dragging == 1:
|
||||
self.ui.popMenu.mouse_is_panning = True
|
||||
return
|
||||
else:
|
||||
self.panning_action = False
|
||||
|
||||
if self.rel_point1 is not None:
|
||||
try: # May fail in case mouse not within axes
|
||||
|
@ -5222,12 +5330,12 @@ class App(QtCore.QObject):
|
|||
# canvas menu
|
||||
try:
|
||||
if event.button == 2: # right click
|
||||
if self.panning_action is True:
|
||||
self.panning_action = False
|
||||
else:
|
||||
if self.ui.popMenu.mouse_is_panning is False:
|
||||
|
||||
self.cursor = QtGui.QCursor()
|
||||
self.populate_cmenu_grids()
|
||||
self.ui.popMenu.popup(self.cursor.pos())
|
||||
|
||||
except Exception as e:
|
||||
log.warning("Error: %s" % str(e))
|
||||
return
|
||||
|
@ -5294,11 +5402,13 @@ class App(QtCore.QObject):
|
|||
if sel_type is True:
|
||||
if poly_obj.within(poly_selection):
|
||||
# create the selection box around the selected object
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(obj)
|
||||
self.collection.set_active(obj.options['name'])
|
||||
else:
|
||||
if poly_selection.intersects(poly_obj):
|
||||
# create the selection box around the selected object
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(obj)
|
||||
self.collection.set_active(obj.options['name'])
|
||||
except:
|
||||
|
@ -5349,6 +5459,7 @@ class App(QtCore.QObject):
|
|||
self.collection.set_active(objects_under_the_click_list[0])
|
||||
# create the selection box around the selected object
|
||||
curr_sel_obj = self.collection.get_active()
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(curr_sel_obj)
|
||||
|
||||
# self.inform.emit('[selected] %s: %s selected' %
|
||||
|
@ -5372,6 +5483,7 @@ class App(QtCore.QObject):
|
|||
self.collection.set_active(objects_under_the_click_list[0])
|
||||
# create the selection box around the selected object
|
||||
curr_sel_obj = self.collection.get_active()
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(curr_sel_obj)
|
||||
|
||||
# self.inform.emit('[selected] %s: %s selected' %
|
||||
|
@ -5420,6 +5532,7 @@ class App(QtCore.QObject):
|
|||
# delete the possible selection box around a possible selected object
|
||||
self.delete_selection_shape()
|
||||
# create the selection box around the selected object
|
||||
if self.defaults['global_selection_shape'] is True:
|
||||
self.draw_selection_shape(curr_sel_obj)
|
||||
|
||||
# self.inform.emit('[selected] %s: %s selected' %
|
||||
|
@ -7597,6 +7710,7 @@ class App(QtCore.QObject):
|
|||
icons = {
|
||||
"gerber": "share/flatcam_icon16.png",
|
||||
"excellon": "share/drill16.png",
|
||||
'geometry': "share/geometry16.png",
|
||||
"cncjob": "share/cnc16.png",
|
||||
"project": "share/project16.png",
|
||||
"svg": "share/geometry16.png",
|
||||
|
@ -7868,23 +7982,23 @@ The normal flow when working in FlatCAM is the following:</span></p>
|
|||
QObject::connect: Cannot queue arguments of type 'QVector<int>'
|
||||
(Make sure 'QVector<int>' is registered using qRegisterMetaType().
|
||||
'''
|
||||
def enable_plots(self, objects, threaded=False):
|
||||
def enable_plots(self, objects, threaded=True):
|
||||
if threaded is True:
|
||||
def worker_task(app_obj):
|
||||
percentage = 0.1
|
||||
try:
|
||||
delta = 0.9 / len(objects)
|
||||
except ZeroDivisionError:
|
||||
self.progress.emit(0)
|
||||
return
|
||||
# percentage = 0.1
|
||||
# try:
|
||||
# delta = 0.9 / len(objects)
|
||||
# except ZeroDivisionError:
|
||||
# self.progress.emit(0)
|
||||
# return
|
||||
for obj in objects:
|
||||
obj.options['plot'] = True
|
||||
percentage += delta
|
||||
self.progress.emit(int(percentage*100))
|
||||
# percentage += delta
|
||||
# self.progress.emit(int(percentage*100))
|
||||
|
||||
self.progress.emit(0)
|
||||
# self.progress.emit(0)
|
||||
self.plots_updated.emit()
|
||||
self.collection.update_view()
|
||||
# self.collection.update_view()
|
||||
|
||||
# Send to worker
|
||||
# self.worker.add_task(worker_task, [self])
|
||||
|
@ -7892,9 +8006,9 @@ The normal flow when working in FlatCAM is the following:</span></p>
|
|||
else:
|
||||
for obj in objects:
|
||||
obj.options['plot'] = True
|
||||
self.progress.emit(0)
|
||||
# self.progress.emit(0)
|
||||
self.plots_updated.emit()
|
||||
self.collection.update_view()
|
||||
# self.collection.update_view()
|
||||
|
||||
# TODO: FIX THIS
|
||||
'''
|
||||
|
@ -7904,7 +8018,7 @@ The normal flow when working in FlatCAM is the following:</span></p>
|
|||
QObject::connect: Cannot queue arguments of type 'QVector<int>'
|
||||
(Make sure 'QVector<int>' is registered using qRegisterMetaType().
|
||||
'''
|
||||
def disable_plots(self, objects, threaded=False):
|
||||
def disable_plots(self, objects, threaded=True):
|
||||
# TODO: This method is very similar to replot_all. Try to merge.
|
||||
"""
|
||||
Disables plots
|
||||
|
@ -7914,23 +8028,23 @@ The normal flow when working in FlatCAM is the following:</span></p>
|
|||
"""
|
||||
|
||||
if threaded is True:
|
||||
self.progress.emit(10)
|
||||
# self.progress.emit(10)
|
||||
def worker_task(app_obj):
|
||||
percentage = 0.1
|
||||
try:
|
||||
delta = 0.9 / len(objects)
|
||||
except ZeroDivisionError:
|
||||
self.progress.emit(0)
|
||||
return
|
||||
# percentage = 0.1
|
||||
# try:
|
||||
# delta = 0.9 / len(objects)
|
||||
# except ZeroDivisionError:
|
||||
# self.progress.emit(0)
|
||||
# return
|
||||
|
||||
for obj in objects:
|
||||
obj.options['plot'] = False
|
||||
percentage += delta
|
||||
self.progress.emit(int(percentage*100))
|
||||
# percentage += delta
|
||||
# self.progress.emit(int(percentage*100))
|
||||
|
||||
self.progress.emit(0)
|
||||
# self.progress.emit(0)
|
||||
self.plots_updated.emit()
|
||||
self.collection.update_view()
|
||||
# self.collection.update_view()
|
||||
|
||||
# Send to worker
|
||||
self.worker_task.emit({'fcn': worker_task, 'params': [self]})
|
||||
|
@ -7938,7 +8052,7 @@ The normal flow when working in FlatCAM is the following:</span></p>
|
|||
for obj in objects:
|
||||
obj.options['plot'] = False
|
||||
self.plots_updated.emit()
|
||||
self.collection.update_view()
|
||||
# self.collection.update_view()
|
||||
|
||||
def clear_plots(self):
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||
old_name = copy(self.options["name"])
|
||||
new_name = self.ui.name_entry.get_value()
|
||||
|
||||
if new_name != old_name:
|
||||
# update the SHELL auto-completer model data
|
||||
try:
|
||||
self.app.myKeywords.remove(old_name)
|
||||
|
@ -358,6 +359,12 @@ class FlatCAMObj(QtCore.QObject):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Not all object types have mark_shapes
|
||||
# try:
|
||||
# self.mark_shapes.clear(update)
|
||||
# except AttributeError:
|
||||
# pass
|
||||
|
||||
def delete(self):
|
||||
# Free resources
|
||||
del self.ui
|
||||
|
@ -2717,7 +2724,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# those elements are the ones used for generating GCode
|
||||
self.sel_tools = {}
|
||||
|
||||
self.offset_item_options = [_("Path"), _("In"), _("Out"), _("Custom")]
|
||||
self.offset_item_options = ["Path", "In", "Out", "Custom"]
|
||||
self.type_item_options = [_("Iso"), _("Rough"), _("Finish")]
|
||||
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
|
||||
|
||||
|
@ -2959,7 +2966,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.tools.update({
|
||||
self.tooluid: {
|
||||
'tooldia': float(self.options["cnctooldia"]),
|
||||
'offset': _('Path'),
|
||||
'offset': ('Path'),
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': 'C1',
|
||||
|
@ -3041,7 +3048,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
tool_offset = self.ui.geo_tools_table.cellWidget(current_row, 2)
|
||||
if tool_offset is not None:
|
||||
tool_offset_txt = tool_offset.currentText()
|
||||
if tool_offset_txt == _('Custom'):
|
||||
if tool_offset_txt == ('Custom'):
|
||||
self.ui.tool_offset_entry.show()
|
||||
self.ui.tool_offset_lbl.show()
|
||||
else:
|
||||
|
@ -3246,7 +3253,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.tools.update({
|
||||
self.tooluid: {
|
||||
'tooldia': tooldia,
|
||||
'offset': _('Path'),
|
||||
'offset': ('Path'),
|
||||
'offset_value': 0.0,
|
||||
'type': _('Rough'),
|
||||
'tool_type': 'C1',
|
||||
|
@ -3615,7 +3622,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
cb_txt = cw.currentText()
|
||||
if cw_col == 2:
|
||||
tooluid_value['offset'] = cb_txt
|
||||
if cb_txt == _('Custom'):
|
||||
if cb_txt == ('Custom'):
|
||||
self.ui.tool_offset_entry.show()
|
||||
self.ui.tool_offset_lbl.show()
|
||||
else:
|
||||
|
@ -4088,13 +4095,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
diadict_key: datadict
|
||||
})
|
||||
|
||||
if dia_cnc_dict['offset'] == 'in':
|
||||
if dia_cnc_dict['offset'] == ('in'):
|
||||
tool_offset = -dia_cnc_dict['tooldia'] / 2
|
||||
offset_str = 'inside'
|
||||
elif dia_cnc_dict['offset'].lower() == 'out':
|
||||
elif dia_cnc_dict['offset'].lower() == ('out'):
|
||||
tool_offset = dia_cnc_dict['tooldia'] / 2
|
||||
offset_str = 'outside'
|
||||
elif dia_cnc_dict['offset'].lower() == 'path':
|
||||
elif dia_cnc_dict['offset'].lower() == ('path'):
|
||||
offset_str = 'onpath'
|
||||
tool_offset = 0.0
|
||||
else:
|
||||
|
@ -4321,13 +4328,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
diadict_key: datadict
|
||||
})
|
||||
|
||||
if dia_cnc_dict['offset'] == 'in':
|
||||
if dia_cnc_dict['offset'] == ('in'):
|
||||
tool_offset = -dia_cnc_dict['tooldia'] / 2
|
||||
offset_str = 'inside'
|
||||
elif dia_cnc_dict['offset'].lower() == 'out':
|
||||
elif dia_cnc_dict['offset'].lower() == ('out'):
|
||||
tool_offset = dia_cnc_dict['tooldia'] / 2
|
||||
offset_str = 'outside'
|
||||
elif dia_cnc_dict['offset'].lower() == 'path':
|
||||
elif dia_cnc_dict['offset'].lower() == ('path'):
|
||||
offset_str = 'onpath'
|
||||
tool_offset = 0.0
|
||||
else:
|
||||
|
|
|
@ -25,7 +25,7 @@ class WorkerStack(QtCore.QObject):
|
|||
thread.started.connect(worker.run)
|
||||
worker.task_completed.connect(self.on_task_completed)
|
||||
|
||||
thread.start()
|
||||
thread.start(QtCore.QThread.LowPriority)
|
||||
|
||||
self.workers.append(worker)
|
||||
self.threads.append(thread)
|
||||
|
|
|
@ -118,13 +118,13 @@ class KeySensitiveListView(QtWidgets.QTreeView):
|
|||
event.ignore()
|
||||
|
||||
|
||||
class TreeItem:
|
||||
class TreeItem(KeySensitiveListView):
|
||||
"""
|
||||
Item of a tree model
|
||||
"""
|
||||
|
||||
def __init__(self, data, icon=None, obj=None, parent_item=None):
|
||||
|
||||
super(TreeItem, self).__init__(parent_item)
|
||||
self.parent_item = parent_item
|
||||
self.item_data = data # Columns string data
|
||||
self.icon = icon # Decoration
|
||||
|
@ -378,9 +378,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
return index.internalPointer().data(index.column())
|
||||
|
||||
if role == Qt.ForegroundRole:
|
||||
color = QColor(self.app.defaults['global_proj_item_color'])
|
||||
color_disabled = QColor(self.app.defaults['global_proj_item_dis_color'])
|
||||
obj = index.internalPointer().obj
|
||||
if obj:
|
||||
return QtGui.QBrush(QtCore.Qt.black) if obj.options["plot"] else QtGui.QBrush(QtCore.Qt.darkGray)
|
||||
return QtGui.QBrush(color) if obj.options["plot"] else QtGui.QBrush(color_disabled)
|
||||
else:
|
||||
return index.internalPointer().data(index.column())
|
||||
|
||||
|
@ -397,10 +399,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
if index.isValid():
|
||||
obj = index.internalPointer().obj
|
||||
if obj:
|
||||
old_name = obj.options['name']
|
||||
old_name = str(obj.options['name'])
|
||||
new_name = str(data)
|
||||
if old_name != new_name and new_name != '':
|
||||
# rename the object
|
||||
obj.options["name"] = str(data)
|
||||
new_name = obj.options['name']
|
||||
|
||||
# update the SHELL auto-completer model data
|
||||
try:
|
||||
|
@ -413,7 +416,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
"setData() --> Could not remove the old object name from auto-completer model list")
|
||||
|
||||
obj.build_ui()
|
||||
self.app.inform.emit(_("Object renamed from {old} to {new}").format(old=old_name, new=new_name))
|
||||
self.app.inform.emit(_("Object renamed from <b>{old}</b> to <b>{new}</b>").format(old=old_name,
|
||||
new=new_name))
|
||||
|
||||
return True
|
||||
|
||||
|
@ -681,6 +685,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
:param name: Name of the FlatCAM Object
|
||||
:return: None
|
||||
"""
|
||||
log.debug("ObjectCollection.set_inactive()")
|
||||
|
||||
obj = self.get_by_name(name)
|
||||
item = obj.item
|
||||
group = self.group_items[obj.kind]
|
||||
|
@ -721,7 +727,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
color='red', name=str(obj.options['name'])))
|
||||
|
||||
except IndexError:
|
||||
FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
|
||||
# FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
|
||||
self.app.inform.emit('')
|
||||
try:
|
||||
self.app.ui.selected_scroll_area.takeWidget()
|
||||
|
|
61
README.md
61
README.md
|
@ -9,6 +9,67 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
01.05.2019
|
||||
|
||||
- the project items color is now controlled from Foreground Role in ObjectCollection.data()
|
||||
- made again plot functions threaded but moved the dataChanged signal (update_view() ) to the main thread by using an already existing signal (plots_updated signal) to avoid the errors with register QVector
|
||||
- Enable/Disable Object toggle key ("Space" key) will trigger also the datChanged signal for the Project MVC
|
||||
- added a new setting for the color of the Project items, the color when they are disabled.
|
||||
- fixed a crash when triggering 'Jump To' menu action (shortcut key 'J' worked ok)
|
||||
- made some mods to what can be translated as some of the translations interfered with the correct functioning of FlatCAM
|
||||
- updated the translations
|
||||
- fixed bugs in Excellon Editor
|
||||
- Excellon Editor: made Add Pad tool to work until right click
|
||||
- Excellon Editor: fixed mouse right click was always doing popup context menu
|
||||
- GUIElements.FCEntry2(): added a try-except clause
|
||||
- made sure that the Tools Tab is cleared on Editors exit
|
||||
- Geometry Editor: restored the old behavior: a tool is active until it is voluntarily exited: either by using the 'ESC' key, or selecting the Select tool or new: right click on canvas
|
||||
- RELEASE 8.915
|
||||
|
||||
30.04.2019
|
||||
|
||||
- in ObjectCollection class, made sure that renaming an object in Project View does not result in an empty name. If new name is blank the rename is cancelled.
|
||||
- made ObjectCollection.TreeItem() inherit KeySensitiveListVIew and implicitly QTreeView (in the hope that the theme applied on app will be applied on the tree items, too (for MacOs new DarkUI theme)
|
||||
- renamed SilkScreen Tool to Substract Tool and move it's menu location in Edit -> Conversion
|
||||
- started to modify the Substract Tool to work on Geometry objects too
|
||||
- progress in the new Substract Tool for Geometry Objects
|
||||
- finished the new Substract Tool
|
||||
- added new setting for the color of the Project Tree items; it helps in providing contrast when using dark theme like the one in MacOS
|
||||
|
||||
29.04.2019
|
||||
|
||||
- solved bug in Gerber Editor: the '0' aperture (the region aperture) had no size which created errors. Made the size to be zero.
|
||||
- solved bug in editors: the canvas selection shape was not deleted on mouse release if the grid snap was OFF
|
||||
- solved bug in Excellon Editor: when selecting a drill hole on canvas the selected row in the Tools Table was not the correct one but the next highest row
|
||||
- finished the Silkscreen Tool but there are some limitations (some wires fragments from silkscreen are lost)
|
||||
- solved the issue in Silkscreen Tool with losing some fragments of wires from silkscreen
|
||||
|
||||
26.04.2019
|
||||
|
||||
- small changes in GUI; optimized contextual menu display
|
||||
- made sure that the Project Tab is disabled while one of the Editors is active and it is restored after returning to app
|
||||
- fixed some bugs recently introduced in Editors due of the changes done to the way mouse panning is detected
|
||||
- cleaned up the context menu's when in Editors; made some structural changes
|
||||
- updated the code in camlib.CNCJob.generate_from_excellon_by_tools() to work with the new API from Google OR-Tools
|
||||
- all Gerber regions (G36 G37) are stored in the '0' aperture
|
||||
- fixed a bug that added geometry with clear polarity in the apertures where was not supposed to be
|
||||
|
||||
25.04.2019
|
||||
|
||||
- Geometry Editor: modified the intersection (if the selected shapes don't intersects preserve them) and substract functions (delete all shapes that were used in the process)
|
||||
- work in the ToolSub
|
||||
- for all objects, if in Selected the object name is changed to the same name, the rename is not done (because there is nothing changed)
|
||||
- fixed Edit -> Copy as Geom function handler to work for Excellon objects, too
|
||||
- made sure that the mouse pointer is restored to default on Editor exit
|
||||
- added a toggle button in Preferences to toggle on/off the display of the selection box on canvas when the user is clicking an object or selecting it by mouse dragging.
|
||||
|
||||
24.04.2019
|
||||
|
||||
- PDF import tool: working in making the PDF layer rendering multithreaded in itself (one layer rendered on each worker)
|
||||
- PDF import tool: solved a bug in parsing the rectangle subpath (an extra point was added to the subpath creating nonexisting geometry)
|
||||
- PDF import tool: finished layer rendering multithreading
|
||||
- New tool: Silkscreen Tool: I am trying to remove the overlapped geo with the soldermask layer from overlay layer; layed out the class and functions - not working yet
|
||||
|
||||
23.04.2019
|
||||
|
||||
- Gerber Editor: added two new tools: Add Disc and Add SemiDisc (porting of Circle and Arc from Geometry Editor)
|
||||
|
|
99
camlib.py
99
camlib.py
|
@ -1942,6 +1942,10 @@ class Gerber (Geometry):
|
|||
# will store the Gerber geometry's as paths
|
||||
self.follow_geometry = []
|
||||
|
||||
# made True when the LPC command is encountered in Gerber parsing
|
||||
# it allows adding data into the clear_geometry key of the self.apertures[aperture] dict
|
||||
self.is_lpc = False
|
||||
|
||||
self.source_file = ''
|
||||
|
||||
# Attributes to be included in serialization
|
||||
|
@ -2174,10 +2178,6 @@ class Gerber (Geometry):
|
|||
# applying a union for every new polygon.
|
||||
poly_buffer = []
|
||||
|
||||
# made True when the LPC command is encountered in Gerber parsing
|
||||
# it allows adding data into the clear_geometry key of the self.apertures[aperture] dict
|
||||
self.is_lpc = False
|
||||
|
||||
# store here the follow geometry
|
||||
follow_buffer = []
|
||||
|
||||
|
@ -2242,6 +2242,8 @@ class Gerber (Geometry):
|
|||
match = self.lpol_re.search(gline)
|
||||
if match:
|
||||
new_polarity = match.group(1)
|
||||
# log.info("Polarity CHANGE, LPC = %s, poly_buff = %s" % (self.is_lpc, poly_buffer))
|
||||
self.is_lpc = True if new_polarity == 'C' else False
|
||||
if len(path) > 1 and current_polarity != new_polarity:
|
||||
|
||||
# finish the current path and add it to the storage
|
||||
|
@ -2258,6 +2260,7 @@ class Gerber (Geometry):
|
|||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
|
@ -2280,12 +2283,10 @@ class Gerber (Geometry):
|
|||
# TODO: Remove when bug fixed
|
||||
if len(poly_buffer) > 0:
|
||||
if current_polarity == 'D':
|
||||
self.is_lpc = True
|
||||
# self.follow_geometry = self.follow_geometry.union(cascaded_union(follow_buffer))
|
||||
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
|
||||
|
||||
else:
|
||||
self.is_lpc = False
|
||||
# self.follow_geometry = self.follow_geometry.difference(cascaded_union(follow_buffer))
|
||||
self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
|
||||
|
||||
|
@ -2414,7 +2415,7 @@ class Gerber (Geometry):
|
|||
### Aperture definitions %ADD...
|
||||
match = self.ad_re.search(gline)
|
||||
if match:
|
||||
log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
|
||||
# log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
|
||||
self.aperture_parse(match.group(1), match.group(2), match.group(3))
|
||||
continue
|
||||
|
||||
|
@ -2460,7 +2461,7 @@ class Gerber (Geometry):
|
|||
match = self.tool_re.search(gline)
|
||||
if match:
|
||||
current_aperture = match.group(1)
|
||||
log.debug("Line %d: Aperture change to (%s)" % (line_num, match.group(1)))
|
||||
# log.debug("Line %d: Aperture change to (%s)" % (line_num, current_aperture))
|
||||
|
||||
# If the aperture value is zero then make it something quite small but with a non-zero value
|
||||
# so it can be processed by FlatCAM.
|
||||
|
@ -2469,7 +2470,7 @@ class Gerber (Geometry):
|
|||
if self.apertures[current_aperture]["type"] is not "AM":
|
||||
if self.apertures[current_aperture]["size"] == 0:
|
||||
self.apertures[current_aperture]["size"] = 1e-12
|
||||
log.debug(self.apertures[current_aperture])
|
||||
# log.debug(self.apertures[current_aperture])
|
||||
|
||||
# Take care of the current path with the previous tool
|
||||
if len(path) > 1:
|
||||
|
@ -2479,7 +2480,6 @@ class Gerber (Geometry):
|
|||
else:
|
||||
# --- Buffered ----
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
||||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
|
@ -2551,6 +2551,12 @@ class Gerber (Geometry):
|
|||
if self.regionoff_re.search(gline):
|
||||
making_region = False
|
||||
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
|
||||
# if D02 happened before G37 we now have a path with 1 element only so we have to add the current
|
||||
# geo to the poly_buffer otherwise we loose it
|
||||
if current_operation_code == 2:
|
||||
|
@ -2558,24 +2564,24 @@ class Gerber (Geometry):
|
|||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
self.apertures['0']['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
self.apertures['0']['follow_geometry'] = []
|
||||
self.apertures['0']['follow_geometry'].append(geo)
|
||||
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
self.apertures['0']['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
self.apertures['0']['clear_geometry'] = []
|
||||
self.apertures['0']['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures['0']['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
self.apertures['0']['solid_geometry'].append(geo)
|
||||
continue
|
||||
|
||||
# Only one path defines region?
|
||||
|
@ -2599,10 +2605,10 @@ class Gerber (Geometry):
|
|||
if not region.is_empty:
|
||||
follow_buffer.append(region)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(region)
|
||||
self.apertures['0']['follow_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(region)
|
||||
self.apertures['0']['follow_geometry'] = []
|
||||
self.apertures['0']['follow_geometry'].append(region)
|
||||
|
||||
region = Polygon(path)
|
||||
if not region.is_valid:
|
||||
|
@ -2613,17 +2619,18 @@ class Gerber (Geometry):
|
|||
|
||||
# we do this for the case that a region is done without having defined any aperture
|
||||
# Allegro does that
|
||||
if current_aperture:
|
||||
used_aperture = current_aperture
|
||||
elif last_path_aperture:
|
||||
used_aperture = last_path_aperture
|
||||
else:
|
||||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
# if current_aperture:
|
||||
# used_aperture = current_aperture
|
||||
# elif last_path_aperture:
|
||||
# used_aperture = last_path_aperture
|
||||
# else:
|
||||
# if '0' not in self.apertures:
|
||||
# self.apertures['0'] = {}
|
||||
# self.apertures['0']['size'] = 0.0
|
||||
# self.apertures['0']['type'] = 'REG'
|
||||
# self.apertures['0']['solid_geometry'] = []
|
||||
# used_aperture = '0'
|
||||
used_aperture = '0'
|
||||
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[used_aperture]['clear_geometry'].append(region)
|
||||
|
@ -2727,6 +2734,7 @@ class Gerber (Geometry):
|
|||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
else:
|
||||
|
@ -2746,6 +2754,7 @@ class Gerber (Geometry):
|
|||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
geo = Polygon()
|
||||
|
@ -2779,6 +2788,7 @@ class Gerber (Geometry):
|
|||
if '0' not in self.apertures:
|
||||
self.apertures['0'] = {}
|
||||
self.apertures['0']['type'] = 'REG'
|
||||
self.apertures['0']['size'] = 0.0
|
||||
self.apertures['0']['solid_geometry'] = []
|
||||
last_path_aperture = '0'
|
||||
elem = [linear_x, linear_y]
|
||||
|
@ -5288,8 +5298,13 @@ class CNCjob(Geometry):
|
|||
y2 = locations[to_node][1]
|
||||
self.matrix[from_node][to_node] = distance_euclidian(x1, y1, x2, y2)
|
||||
|
||||
def Distance(self, from_node, to_node):
|
||||
return int(self.matrix[from_node][to_node])
|
||||
# def Distance(self, from_node, to_node):
|
||||
# return int(self.matrix[from_node][to_node])
|
||||
def Distance(self, from_index, to_index):
|
||||
# Convert from routing variable Index to distance matrix NodeIndex.
|
||||
from_node = manager.IndexToNode(from_index)
|
||||
to_node = manager.IndexToNode(to_index)
|
||||
return self.matrix[from_node][to_node]
|
||||
|
||||
# Create the data.
|
||||
def create_data_array():
|
||||
|
@ -5327,8 +5342,9 @@ class CNCjob(Geometry):
|
|||
depot = 0
|
||||
# Create routing model.
|
||||
if tsp_size > 0:
|
||||
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
manager = pywrapcp.RoutingIndexManager(tsp_size, num_routes, depot)
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.local_search_metaheuristic = (
|
||||
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
|
||||
|
||||
|
@ -5343,7 +5359,8 @@ class CNCjob(Geometry):
|
|||
# arguments (the from and to node indices) and returns the distance between them.
|
||||
dist_between_locations = CreateDistanceCallback()
|
||||
dist_callback = dist_between_locations.Distance
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
|
||||
transit_callback_index = routing.RegisterTransitCallback(dist_callback)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
|
||||
|
||||
# Solve, returns a solution if any.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
|
@ -5432,14 +5449,16 @@ class CNCjob(Geometry):
|
|||
|
||||
# Create routing model.
|
||||
if tsp_size > 0:
|
||||
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
manager = pywrapcp.RoutingIndexManager(tsp_size, num_routes, depot)
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
|
||||
# Callback to the distance function. The callback takes two
|
||||
# arguments (the from and to node indices) and returns the distance between them.
|
||||
dist_between_locations = CreateDistanceCallback()
|
||||
dist_callback = dist_between_locations.Distance
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
|
||||
transit_callback_index = routing.RegisterTransitCallback(dist_callback)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
|
||||
|
||||
# Solve, returns a solution if any.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
|
|
|
@ -44,7 +44,7 @@ class FCDrillAdd(FCShapeTool):
|
|||
|
||||
except KeyError:
|
||||
self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add a drill first select a tool"))
|
||||
self.draw_app.select_tool("select")
|
||||
self.draw_app.select_tool("drill_select")
|
||||
return
|
||||
|
||||
try:
|
||||
|
@ -103,6 +103,7 @@ class FCDrillAdd(FCShapeTool):
|
|||
|
||||
self.draw_app.current_storage = self.draw_app.storage_dict[self.selected_dia]
|
||||
self.geometry = DrawToolShape(self.util_shape(self.points))
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit(_("[success] Done. Drill added."))
|
||||
|
||||
|
@ -319,7 +320,7 @@ class FCDrillArray(FCShapeTool):
|
|||
self.geometry.append(DrawToolShape(geo))
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit(_("[success] Done. Drill Array added."))
|
||||
self.draw_app.in_action = True
|
||||
self.draw_app.in_action = False
|
||||
self.draw_app.array_frame.hide()
|
||||
return
|
||||
|
||||
|
@ -428,7 +429,7 @@ class FCDrillResize(FCShapeTool):
|
|||
self.complete = True
|
||||
|
||||
# MS: always return to the Select Tool
|
||||
self.draw_app.select_tool("select")
|
||||
self.draw_app.select_tool("drill_select")
|
||||
|
||||
|
||||
class FCDrillMove(FCShapeTool):
|
||||
|
@ -475,7 +476,7 @@ class FCDrillMove(FCShapeTool):
|
|||
self.make()
|
||||
|
||||
# MS: always return to the Select Tool
|
||||
self.draw_app.select_tool("select")
|
||||
self.draw_app.select_tool("drill_select")
|
||||
return
|
||||
|
||||
def make(self):
|
||||
|
@ -643,8 +644,11 @@ class FCDrillSelect(DrawTool):
|
|||
sel_tools.add(storage)
|
||||
|
||||
for storage in sel_tools:
|
||||
self.exc_editor_app.tools_table_exc.selectRow(int(storage))
|
||||
self.draw_app.last_tool_selected = int(storage)
|
||||
for k, v in self.draw_app.tool2tooldia.items():
|
||||
if v == storage:
|
||||
self.exc_editor_app.tools_table_exc.selectRow(int(k) - 1)
|
||||
self.draw_app.last_tool_selected = int(k)
|
||||
break
|
||||
|
||||
self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
|
||||
|
@ -931,7 +935,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
self.drill_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
|
||||
{'label': 'Y', 'value': 'Y'},
|
||||
{'label': _('Angle'), 'value': 'A'}])
|
||||
{'label': 'Angle', 'value': 'A'}])
|
||||
self.drill_axis_radio.set_value('X')
|
||||
self.linear_form.addRow(self.drill_axis_label, self.drill_axis_radio)
|
||||
|
||||
|
@ -999,7 +1003,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
## Toolbar events and properties
|
||||
self.tools_exc = {
|
||||
"select": {"button": self.app.ui.select_drill_btn,
|
||||
"drill_select": {"button": self.app.ui.select_drill_btn,
|
||||
"constructor": FCDrillSelect},
|
||||
"drill_add": {"button": self.app.ui.add_drill_btn,
|
||||
"constructor": FCDrillAdd},
|
||||
|
@ -1016,6 +1020,8 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
### Data
|
||||
self.active_tool = None
|
||||
|
||||
self.in_action = False
|
||||
|
||||
self.storage_dict = {}
|
||||
self.current_storage = []
|
||||
|
||||
|
@ -1067,7 +1073,6 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
self.app.ui.exc_move_drill_menuitem.triggered.connect(self.exc_move_drills)
|
||||
|
||||
|
||||
# Init GUI
|
||||
self.drill_array_size_entry.set_value(5)
|
||||
self.drill_pitch_entry.set_value(2.54)
|
||||
|
@ -1646,6 +1651,13 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
if self.app.ui.grid_snap_btn.isChecked() is False:
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(False)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||
self.app.ui.popmenu_properties.setVisible(False)
|
||||
self.app.ui.e_editor_cmenu.menuAction().setVisible(True)
|
||||
self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
||||
|
||||
# Tell the App that the editor is active
|
||||
self.editor_active = True
|
||||
|
||||
|
@ -1653,6 +1665,11 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.drills_frame.show()
|
||||
|
||||
def deactivate(self):
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
except:
|
||||
pass
|
||||
|
||||
# adjust the status of the menu entries related to the editor
|
||||
self.app.ui.menueditedit.setDisabled(False)
|
||||
self.app.ui.menueditok.setDisabled(True)
|
||||
|
@ -1706,8 +1723,12 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
self.app.ui.update_obj_btn.setEnabled(False)
|
||||
|
||||
self.app.ui.g_editor_cmenu.setEnabled(False)
|
||||
self.app.ui.e_editor_cmenu.setEnabled(False)
|
||||
self.app.ui.popmenu_disable.setVisible(True)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
|
||||
self.app.ui.popmenu_properties.setVisible(True)
|
||||
self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
||||
|
||||
# Show original geometry
|
||||
if self.exc_obj:
|
||||
|
@ -1733,6 +1754,18 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
|
||||
self.app.collection.view.clicked.disconnect()
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.disconnect()
|
||||
self.app.ui.popmenu_delete.triggered.disconnect()
|
||||
self.app.ui.popmenu_move.triggered.disconnect()
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(self.exc_copy_drills)
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
|
||||
self.app.ui.popmenu_move.triggered.connect(self.exc_move_drills)
|
||||
|
||||
# Excellon Editor
|
||||
self.app.ui.drill.triggered.connect(self.exc_add_drill)
|
||||
self.app.ui.drill_array.triggered.connect(self.exc_add_drill_array)
|
||||
|
||||
def disconnect_canvas_event_handlers(self):
|
||||
# we restore the key and mouse control to FlatCAMApp method
|
||||
# first connect to new, then disconnect the old handlers
|
||||
|
@ -1747,6 +1780,36 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
|
||||
self.canvas.vis_disconnect('mouse_release', self.on_exc_click_release)
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_copy.triggered.disconnect(self.exc_copy_drills)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_move.triggered.disconnect(self.exc_move_drills)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
|
||||
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
|
||||
|
||||
# Excellon Editor
|
||||
try:
|
||||
self.app.ui.drill.triggered.disconnect(self.exc_add_drill)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.drill_array.triggered.disconnect(self.exc_add_drill_array)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def clear(self):
|
||||
self.active_tool = None
|
||||
# self.shape_buffer = []
|
||||
|
@ -1786,7 +1849,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
# Set selection tolerance
|
||||
# DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
|
||||
|
||||
self.select_tool("select")
|
||||
self.select_tool("drill_select")
|
||||
|
||||
self.set_ui()
|
||||
|
||||
|
@ -2002,10 +2065,10 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
self.app.log.debug("on_tool_select('%s')" % tool)
|
||||
|
||||
if self.last_tool_selected is None and current_tool is not 'select':
|
||||
# self.draw_app.select_tool('select')
|
||||
if self.last_tool_selected is None and current_tool is not 'drill_select':
|
||||
# self.draw_app.select_tool('drill_select')
|
||||
self.complete = True
|
||||
current_tool = 'select'
|
||||
current_tool = 'drill_select'
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. There is no Tool/Drill selected"))
|
||||
|
||||
# This is to make the group behave as radio group
|
||||
|
@ -2024,7 +2087,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
for t in self.tools_exc:
|
||||
self.tools_exc[t]["button"].setChecked(False)
|
||||
|
||||
self.select_tool('select')
|
||||
self.select_tool('drill_select')
|
||||
self.active_tool = FCDrillSelect(self)
|
||||
|
||||
def on_row_selected(self, row, col):
|
||||
|
@ -2042,7 +2105,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
try:
|
||||
selected_dia = self.tool2tooldia[self.tools_table_exc.currentRow() + 1]
|
||||
self.last_tool_selected = copy(self.tools_table_exc.currentRow()) + 1
|
||||
self.last_tool_selected = int(self.tools_table_exc.currentRow()) + 1
|
||||
for obj in self.storage_dict[selected_dia].get_objects():
|
||||
self.selected.append(obj)
|
||||
except Exception as e:
|
||||
|
@ -2060,23 +2123,29 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
def on_canvas_click(self, event):
|
||||
"""
|
||||
event.x and .y have canvas coordinates
|
||||
event.xdaya and .ydata have plot coordinates
|
||||
event.xdata and .ydata have plot coordinates
|
||||
|
||||
:param event: Event object dispatched by Matplotlib
|
||||
:param event: Event object dispatched by VisPy
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
if self.app.grid_status():
|
||||
self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(self.pos[0], self.pos[1])]), symbol='++', edge_color='black',
|
||||
size=20)
|
||||
else:
|
||||
self.pos = (self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
if event.button is 1:
|
||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||
"%.4f " % (0, 0))
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
### Snap coordinates
|
||||
x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
||||
|
||||
self.pos = (x, y)
|
||||
# print(self.active_tool)
|
||||
|
||||
# Selection with left mouse button
|
||||
if self.active_tool is not None and event.button is 1:
|
||||
# Dispatch event to active_tool
|
||||
|
@ -2100,7 +2169,11 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
if key_modifier == modifier_to_use:
|
||||
self.select_tool(self.active_tool.name)
|
||||
else:
|
||||
self.select_tool("select")
|
||||
# return to Select tool but not for FCPad
|
||||
if isinstance(self.active_tool, FCDrillAdd):
|
||||
self.select_tool(self.active_tool.name)
|
||||
else:
|
||||
self.select_tool("drill_select")
|
||||
return
|
||||
|
||||
if isinstance(self.active_tool, FCDrillSelect):
|
||||
|
@ -2187,10 +2260,9 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.storage.insert(shape) # TODO: Check performance
|
||||
|
||||
def on_exc_click_release(self, event):
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
self.modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
if self.app.grid_status():
|
||||
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
else:
|
||||
|
@ -2200,11 +2272,29 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
# canvas menu
|
||||
try:
|
||||
if event.button == 2: # right click
|
||||
if self.app.panning_action is True:
|
||||
self.app.panning_action = False
|
||||
if self.app.ui.popMenu.mouse_is_panning is False:
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
except:
|
||||
pass
|
||||
if self.active_tool.complete is False and not isinstance(self.active_tool, FCDrillSelect):
|
||||
self.active_tool.complete = True
|
||||
self.in_action = False
|
||||
self.delete_utility_geometry()
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
self.select_tool('drill_select')
|
||||
else:
|
||||
if isinstance(self.active_tool, FCDrillAdd):
|
||||
self.active_tool.complete = True
|
||||
self.in_action = False
|
||||
self.delete_utility_geometry()
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
self.select_tool('drill_select')
|
||||
|
||||
self.app.cursor = QtGui.QCursor()
|
||||
self.app.populate_cmenu_grids()
|
||||
self.app.ui.popMenu.popup(self.app.cursor.pos())
|
||||
|
||||
except Exception as e:
|
||||
log.warning("Error: %s" % str(e))
|
||||
raise
|
||||
|
@ -2216,12 +2306,12 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
if self.app.selection_type is not None:
|
||||
self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
|
||||
self.app.selection_type = None
|
||||
|
||||
elif isinstance(self.active_tool, FCDrillSelect):
|
||||
# Dispatch event to active_tool
|
||||
# msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
|
||||
# msg = self.active_tool.click_release((self.pos[0], self.pos[1]))
|
||||
# self.app.inform.emit(msg)
|
||||
self.active_tool.click_release((self.pos[0], self.pos[1]))
|
||||
|
||||
# if there are selected objects then plot them
|
||||
if self.selected:
|
||||
self.replot()
|
||||
except Exception as e:
|
||||
log.warning("Error: %s" % str(e))
|
||||
|
@ -2264,7 +2354,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
if self.tool2tooldia[key] == storage:
|
||||
item = self.tools_table_exc.item((key - 1), 1)
|
||||
self.tools_table_exc.setCurrentItem(item)
|
||||
self.last_tool_selected = key
|
||||
self.last_tool_selected = int(key)
|
||||
|
||||
self.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
|
||||
|
@ -2287,16 +2377,12 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.x = event.xdata
|
||||
self.y = event.ydata
|
||||
|
||||
# Prevent updates on pan
|
||||
# if len(event.buttons) > 0:
|
||||
# return
|
||||
self.app.ui.popMenu.mouse_is_panning = False
|
||||
|
||||
# if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
||||
if event.button == 2:
|
||||
self.app.panning_action = True
|
||||
if event.button == 2 and event.is_dragging == 1:
|
||||
self.app.ui.popMenu.mouse_is_panning = True
|
||||
return
|
||||
else:
|
||||
self.app.panning_action = False
|
||||
|
||||
try:
|
||||
x = float(event.xdata)
|
||||
|
@ -2308,7 +2394,13 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
return
|
||||
|
||||
### Snap coordinates
|
||||
x, y = self.app.geo_editor.app.geo_editor.snap(x, y)
|
||||
if self.app.grid_status():
|
||||
x, y = self.app.geo_editor.snap(x, y)
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
|
||||
else:
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
self.snap_x = x
|
||||
self.snap_y = y
|
||||
|
@ -2330,14 +2422,18 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
geo = self.active_tool.utility_geometry(data=(x, y))
|
||||
|
||||
if isinstance(geo, DrawToolShape) and geo.geo is not None:
|
||||
|
||||
# Remove any previous utility shape
|
||||
self.tool_shape.clear(update=True)
|
||||
self.draw_utility_geometry(geo=geo)
|
||||
|
||||
### Selection area on canvas section ###
|
||||
dx = pos[0] - self.pos[0]
|
||||
if event.is_dragging == 1 and event.button == 1:
|
||||
# I make an exception for FCDrillAdd and FCDrillArray because clicking and dragging while making regions
|
||||
# can create strange issues
|
||||
if isinstance(self.active_tool, FCDrillAdd) or isinstance(self.active_tool, FCDrillArray):
|
||||
pass
|
||||
else:
|
||||
dx = pos[0] - self.pos[0]
|
||||
self.app.delete_selection_shape()
|
||||
if dx < 0:
|
||||
self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y),
|
||||
|
@ -2587,21 +2683,21 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.linear_angle_label.hide()
|
||||
|
||||
def exc_add_drill(self):
|
||||
self.select_tool('add')
|
||||
self.select_tool('drill_add')
|
||||
return
|
||||
|
||||
def exc_add_drill_array(self):
|
||||
self.select_tool('add_array')
|
||||
self.select_tool('drill_array')
|
||||
return
|
||||
|
||||
def exc_resize_drills(self):
|
||||
self.select_tool('resize')
|
||||
self.select_tool('drill_resize')
|
||||
return
|
||||
|
||||
def exc_copy_drills(self):
|
||||
self.select_tool('copy')
|
||||
self.select_tool('drill_copy')
|
||||
return
|
||||
|
||||
def exc_move_drills(self):
|
||||
self.select_tool('move')
|
||||
self.select_tool('drill_move')
|
||||
return
|
|
@ -18,7 +18,7 @@ from FlatCAMTool import FlatCAMTool
|
|||
from flatcamGUI.ObjectUI import LengthEntry, RadioSet
|
||||
|
||||
from shapely.geometry import LineString, LinearRing, MultiLineString
|
||||
from shapely.ops import cascaded_union
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
import shapely.affinity as affinity
|
||||
|
||||
from numpy import arctan2, Inf, array, sqrt, sign, dot
|
||||
|
@ -475,9 +475,9 @@ class PaintOptionsTool(FlatCAMTool):
|
|||
)
|
||||
grid.addWidget(methodlabel, 3, 0)
|
||||
self.paintmethod_combo = RadioSet([
|
||||
{"label": _("Standard"), "value": "standard"},
|
||||
{"label": _("Seed-based"), "value": "seed"},
|
||||
{"label": _("Straight lines"), "value": "lines"}
|
||||
{"label": "Standard", "value": "standard"},
|
||||
{"label": "Seed-based", "value": "seed"},
|
||||
{"label": "Straight lines", "value": "lines"}
|
||||
], orientation='vertical', stretch=False)
|
||||
grid.addWidget(self.paintmethod_combo, 3, 1)
|
||||
|
||||
|
@ -2875,8 +2875,12 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
def gridx_changed(goption, gentry):
|
||||
entry2option(option=goption, entry=gentry)
|
||||
# if the grid link is checked copy the value in the GridX field to GridY
|
||||
try:
|
||||
val = float(self.app.ui.grid_gap_x_entry.get_value())
|
||||
except ValueError:
|
||||
return
|
||||
if self.app.ui.grid_gap_link_cb.isChecked():
|
||||
self.app.ui.grid_gap_y_entry.set_value(self.app.ui.grid_gap_x_entry.get_value())
|
||||
self.app.ui.grid_gap_y_entry.set_value(val)
|
||||
|
||||
self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
|
||||
self.app.ui.grid_gap_x_entry.textChanged.connect(
|
||||
|
@ -2970,6 +2974,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
|
||||
self.app.ui.snap_toolbar.setDisabled(False)
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(False)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||
self.app.ui.popmenu_properties.setVisible(False)
|
||||
self.app.ui.g_editor_cmenu.menuAction().setVisible(True)
|
||||
|
||||
# prevent the user to change anything in the Selected Tab while the Geo Editor is active
|
||||
sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
|
||||
for w in sel_tab_widget_list:
|
||||
|
@ -2979,6 +2988,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.editor_active = True
|
||||
|
||||
def deactivate(self):
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
except:
|
||||
pass
|
||||
|
||||
# adjust the status of the menu entries related to the editor
|
||||
self.app.ui.menueditedit.setDisabled(False)
|
||||
self.app.ui.menueditok.setDisabled(True)
|
||||
|
@ -3033,6 +3047,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
# Tell the app that the editor is no longer active
|
||||
self.editor_active = False
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(True)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
|
||||
self.app.ui.popmenu_properties.setVisible(True)
|
||||
self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
||||
|
||||
try:
|
||||
# re-enable all the widgets in the Selected Tab that were disabled after entering in Edit Geometry Mode
|
||||
sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
|
||||
|
@ -3061,7 +3082,21 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
|
||||
|
||||
self.app.collection.view.clicked.disconnect()
|
||||
# self.app.collection.view.clicked.disconnect()
|
||||
self.app.ui.popmenu_copy.triggered.disconnect()
|
||||
self.app.ui.popmenu_delete.triggered.disconnect()
|
||||
self.app.ui.popmenu_move.triggered.disconnect()
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(lambda: self.select_tool('copy'))
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
|
||||
self.app.ui.popmenu_move.triggered.connect(lambda: self.select_tool('move'))
|
||||
|
||||
# Geometry Editor
|
||||
self.app.ui.draw_line.triggered.connect(self.draw_tool_path)
|
||||
self.app.ui.draw_rect.triggered.connect(self.draw_tool_rectangle)
|
||||
self.app.ui.draw_cut.triggered.connect(self.cutpath)
|
||||
self.app.ui.draw_move.triggered.connect(self.on_move)
|
||||
|
||||
|
||||
def disconnect_canvas_event_handlers(self):
|
||||
# we restore the key and mouse control to FlatCAMApp method
|
||||
|
@ -3071,12 +3106,50 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
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.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
|
||||
self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
|
||||
# self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
|
||||
|
||||
self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
|
||||
self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
|
||||
self.canvas.vis_disconnect('mouse_release', self.on_geo_click_release)
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_copy.triggered.disconnect(lambda: self.select_tool('copy'))
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
self.app.ui.popmenu_move.triggered.disconnect(lambda: self.select_tool('move'))
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
|
||||
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
|
||||
|
||||
# Geometry Editor
|
||||
try:
|
||||
self.app.ui.draw_line.triggered.disconnect(self.draw_tool_path)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.draw_rect.triggered.disconnect(self.draw_tool_rectangle)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.draw_cut.triggered.disconnect(self.cutpath)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.draw_move.triggered.disconnect(self.on_move)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def add_shape(self, shape):
|
||||
"""
|
||||
Adds a shape to the shape storage.
|
||||
|
@ -3117,31 +3190,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.tool_shape.clear(update=True)
|
||||
self.tool_shape.redraw()
|
||||
|
||||
def cutpath(self):
|
||||
selected = self.get_selected()
|
||||
tools = selected[1:]
|
||||
toolgeo = cascaded_union([shp.geo for shp in tools])
|
||||
|
||||
target = selected[0]
|
||||
if type(target.geo) == Polygon:
|
||||
for ring in poly2rings(target.geo):
|
||||
self.add_shape(DrawToolShape(ring.difference(toolgeo)))
|
||||
self.delete_shape(target)
|
||||
elif type(target.geo) == LineString or type(target.geo) == LinearRing:
|
||||
self.add_shape(DrawToolShape(target.geo.difference(toolgeo)))
|
||||
self.delete_shape(target)
|
||||
elif type(target.geo) == MultiLineString:
|
||||
try:
|
||||
for linestring in target.geo:
|
||||
self.add_shape(DrawToolShape(linestring.difference(toolgeo)))
|
||||
except:
|
||||
self.app.log.warning("Current LinearString does not intersect the target")
|
||||
self.delete_shape(target)
|
||||
else:
|
||||
self.app.log.warning("Not implemented. Object type: %s" % str(type(target.geo)))
|
||||
|
||||
self.replot()
|
||||
|
||||
def toolbar_tool_toggle(self, key):
|
||||
self.options[key] = self.sender().isChecked()
|
||||
if self.options[key] == True:
|
||||
|
@ -3268,15 +3316,21 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
:return: None
|
||||
"""
|
||||
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
if self.app.grid_status():
|
||||
self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(self.pos[0], self.pos[1])]), symbol='++', edge_color='black',
|
||||
size=20)
|
||||
else:
|
||||
self.pos = (self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
if event.button is 1:
|
||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||
"%.4f " % (0, 0))
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
### Snap coordinates
|
||||
x, y = self.snap(self.pos[0], self.pos[1])
|
||||
|
||||
self.pos = (x, y)
|
||||
|
||||
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
|
||||
|
@ -3288,7 +3342,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
# Selection with left mouse button
|
||||
if self.active_tool is not None and event.button is 1:
|
||||
# Dispatch event to active_tool
|
||||
# msg = self.active_tool.click(self.snap(event.xdata, event.ydata))
|
||||
msg = self.active_tool.click(self.snap(self.pos[0], self.pos[1]))
|
||||
|
||||
# If it is a shape generating tool
|
||||
|
@ -3303,13 +3356,19 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
else:
|
||||
modifier_to_use = Qt.ShiftModifier
|
||||
|
||||
if isinstance(self.active_tool, FCText):
|
||||
self.select_tool("select")
|
||||
else:
|
||||
self.select_tool(self.active_tool.name)
|
||||
|
||||
|
||||
# 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 == modifier_to_use:
|
||||
self.select_tool(self.active_tool.name)
|
||||
else:
|
||||
self.select_tool("select")
|
||||
return
|
||||
# if key_modifier == modifier_to_use:
|
||||
# self.select_tool(self.active_tool.name)
|
||||
# else:
|
||||
# self.select_tool("select")
|
||||
# return
|
||||
|
||||
if isinstance(self.active_tool, FCSelect):
|
||||
# self.app.log.debug("Replotting after click.")
|
||||
|
@ -3334,16 +3393,12 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.x = event.xdata
|
||||
self.y = event.ydata
|
||||
|
||||
# Prevent updates on pan
|
||||
# if len(event.buttons) > 0:
|
||||
# return
|
||||
self.app.ui.popMenu.mouse_is_panning = False
|
||||
|
||||
# if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
||||
if event.button == 2:
|
||||
self.app.panning_action = True
|
||||
if event.button == 2 and event.is_dragging == 1:
|
||||
self.app.ui.popMenu.mouse_is_panning = True
|
||||
return
|
||||
else:
|
||||
self.app.panning_action = False
|
||||
|
||||
try:
|
||||
x = float(event.xdata)
|
||||
|
@ -3355,7 +3410,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
return
|
||||
|
||||
### Snap coordinates
|
||||
if self.app.grid_status():
|
||||
x, y = self.snap(x, y)
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
|
||||
else:
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
self.snap_x = x
|
||||
self.snap_y = y
|
||||
|
@ -3396,9 +3457,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
else:
|
||||
self.app.selection_type = None
|
||||
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
|
||||
|
||||
def on_geo_click_release(self, event):
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
|
@ -3411,11 +3469,22 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
# canvas menu
|
||||
try:
|
||||
if event.button == 2: # right click
|
||||
if self.app.panning_action is True:
|
||||
self.app.panning_action = False
|
||||
else:
|
||||
if self.app.ui.popMenu.mouse_is_panning is False:
|
||||
if self.in_action is False:
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
except:
|
||||
pass
|
||||
|
||||
if self.active_tool.complete is False and not isinstance(self.active_tool, FCSelect):
|
||||
self.active_tool.complete = True
|
||||
self.in_action = False
|
||||
self.delete_utility_geometry()
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
self.select_tool('select')
|
||||
else:
|
||||
self.app.cursor = QtGui.QCursor()
|
||||
self.app.populate_cmenu_grids()
|
||||
self.app.ui.popMenu.popup(self.app.cursor.pos())
|
||||
else:
|
||||
# if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
||||
|
@ -3426,19 +3495,20 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
if self.active_tool.complete:
|
||||
self.on_shape_complete()
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
self.select_tool(self.active_tool.name)
|
||||
|
||||
# MS: always return to the Select Tool if modifier key is not pressed
|
||||
# else return to the current tool
|
||||
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
if self.app.defaults["global_mselect_key"] == 'Control':
|
||||
modifier_to_use = Qt.ControlModifier
|
||||
else:
|
||||
modifier_to_use = Qt.ShiftModifier
|
||||
|
||||
if key_modifier == modifier_to_use:
|
||||
self.select_tool(self.active_tool.name)
|
||||
else:
|
||||
self.select_tool("select")
|
||||
# key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
# if self.app.defaults["global_mselect_key"] == 'Control':
|
||||
# modifier_to_use = Qt.ControlModifier
|
||||
# else:
|
||||
# modifier_to_use = Qt.ShiftModifier
|
||||
#
|
||||
# if key_modifier == modifier_to_use:
|
||||
# self.select_tool(self.active_tool.name)
|
||||
# else:
|
||||
# self.select_tool("select")
|
||||
|
||||
except Exception as e:
|
||||
log.warning("Error: %s" % str(e))
|
||||
|
@ -3781,7 +3851,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
:return: None.
|
||||
"""
|
||||
|
||||
results = cascaded_union([t.geo for t in self.get_selected()])
|
||||
results = unary_union([t.geo for t in self.get_selected()])
|
||||
|
||||
# Delete originals.
|
||||
for_deletion = [s for s in self.get_selected()]
|
||||
|
@ -3795,9 +3865,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
|
||||
self.replot()
|
||||
|
||||
def intersection(self):
|
||||
def intersection_2(self):
|
||||
"""
|
||||
Makes intersectino of selected polygons. Original polygons are deleted.
|
||||
Makes intersection of selected polygons. Original polygons are deleted.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
@ -3827,11 +3897,67 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
|
||||
self.replot()
|
||||
|
||||
def intersection(self):
|
||||
"""
|
||||
Makes intersection of selected polygons. Original polygons are deleted.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
shapes = self.get_selected()
|
||||
results = []
|
||||
intact = []
|
||||
|
||||
try:
|
||||
intersector = shapes[0].geo
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGeoEditor.intersection() --> %s" % str(e))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] A selection of at least 2 geo items is required to do Intersection."))
|
||||
self.select_tool('select')
|
||||
return
|
||||
|
||||
for shape in shapes[1:]:
|
||||
if intersector.intersects(shape.geo):
|
||||
results.append(intersector.intersection(shape.geo))
|
||||
else:
|
||||
intact.append(shape)
|
||||
|
||||
if len(results) != 0:
|
||||
# Delete originals.
|
||||
for_deletion = [s for s in self.get_selected()]
|
||||
for shape in for_deletion:
|
||||
if shape not in intact:
|
||||
self.delete_shape(shape)
|
||||
|
||||
for geo in results:
|
||||
self.add_shape(DrawToolShape(geo))
|
||||
|
||||
# Selected geometry is now gone!
|
||||
self.selected = []
|
||||
self.replot()
|
||||
|
||||
def subtract(self):
|
||||
selected = self.get_selected()
|
||||
try:
|
||||
tools = selected[1:]
|
||||
toolgeo = cascaded_union([shp.geo for shp in tools])
|
||||
toolgeo = unary_union([shp.geo for shp in tools])
|
||||
result = selected[0].geo.difference(toolgeo)
|
||||
|
||||
for_deletion = [s for s in self.get_selected()]
|
||||
for shape in for_deletion:
|
||||
self.delete_shape(shape)
|
||||
|
||||
self.add_shape(DrawToolShape(result))
|
||||
|
||||
self.replot()
|
||||
except Exception as e:
|
||||
log.debug(str(e))
|
||||
|
||||
def subtract_2(self):
|
||||
selected = self.get_selected()
|
||||
try:
|
||||
tools = selected[1:]
|
||||
toolgeo = unary_union([shp.geo for shp in tools])
|
||||
result = selected[0].geo.difference(toolgeo)
|
||||
|
||||
self.delete_shape(selected[0])
|
||||
|
@ -3841,6 +3967,30 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
except Exception as e:
|
||||
log.debug(str(e))
|
||||
|
||||
def cutpath(self):
|
||||
selected = self.get_selected()
|
||||
tools = selected[1:]
|
||||
toolgeo = unary_union([shp.geo for shp in tools])
|
||||
|
||||
target = selected[0]
|
||||
if type(target.geo) == Polygon:
|
||||
for ring in poly2rings(target.geo):
|
||||
self.add_shape(DrawToolShape(ring.difference(toolgeo)))
|
||||
elif type(target.geo) == LineString or type(target.geo) == LinearRing:
|
||||
self.add_shape(DrawToolShape(target.geo.difference(toolgeo)))
|
||||
elif type(target.geo) == MultiLineString:
|
||||
try:
|
||||
for linestring in target.geo:
|
||||
self.add_shape(DrawToolShape(linestring.difference(toolgeo)))
|
||||
except:
|
||||
self.app.log.warning("Current LinearString does not intersect the target")
|
||||
else:
|
||||
self.app.log.warning("Not implemented. Object type: %s" % str(type(target.geo)))
|
||||
return
|
||||
|
||||
self.delete_shape(target)
|
||||
self.replot()
|
||||
|
||||
def buffer(self, buf_distance, join_style):
|
||||
selected = self.get_selected()
|
||||
|
||||
|
|
|
@ -1613,6 +1613,7 @@ class FCApertureSelect(DrawTool):
|
|||
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
||||
for storage in self.grb_editor_app.storage_dict:
|
||||
try:
|
||||
for shape in self.grb_editor_app.storage_dict[storage]['solid_geometry']:
|
||||
if Point(point).within(shape.geo):
|
||||
if (self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control' and
|
||||
|
@ -1629,6 +1630,8 @@ class FCApertureSelect(DrawTool):
|
|||
else:
|
||||
self.draw_app.selected.append(shape)
|
||||
sel_aperture.add(storage)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# select the aperture in the Apertures Table that is associated with the selected shape
|
||||
try:
|
||||
|
@ -1980,7 +1983,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
self.pad_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
|
||||
{'label': 'Y', 'value': 'Y'},
|
||||
{'label': _('Angle'), 'value': 'A'}])
|
||||
{'label': 'Angle', 'value': 'A'}])
|
||||
self.pad_axis_radio.set_value('X')
|
||||
self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
|
||||
|
||||
|
@ -2174,11 +2177,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
# flag to show if the object was modified
|
||||
self.is_modified = False
|
||||
|
||||
self.edited_obj_name = ""
|
||||
|
||||
self.tool_row = 0
|
||||
|
||||
# A QTimer
|
||||
self.plot_thread = None
|
||||
|
||||
# store the status of the editor so the Delete at object level will not work until the edit is finished
|
||||
self.editor_active = False
|
||||
|
||||
|
@ -2629,14 +2633,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.edited_obj_name = self.name_entry.get_value()
|
||||
|
||||
def on_aptype_changed(self, current_text):
|
||||
# 'O' is letter O not zero.
|
||||
if current_text == 'R' or current_text == 'O':
|
||||
self.apdim_lbl.show()
|
||||
self.apdim_entry.show()
|
||||
self.apsize_entry.setReadOnly(True)
|
||||
self.apsize_entry.setDisabled(True)
|
||||
else:
|
||||
self.apdim_lbl.hide()
|
||||
self.apdim_entry.hide()
|
||||
self.apsize_entry.setReadOnly(False)
|
||||
self.apsize_entry.setDisabled(False)
|
||||
|
||||
def activate_grb_editor(self):
|
||||
# adjust the status of the menu entries related to the editor
|
||||
|
@ -2684,10 +2689,20 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.app.ui.popmenu_edit.setVisible(False)
|
||||
self.app.ui.popmenu_save.setVisible(True)
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(False)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
||||
self.app.ui.popmenu_properties.setVisible(False)
|
||||
self.app.ui.grb_editor_cmenu.menuAction().setVisible(True)
|
||||
|
||||
# Tell the App that the editor is active
|
||||
self.editor_active = True
|
||||
|
||||
def deactivate_grb_editor(self):
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
except:
|
||||
pass
|
||||
|
||||
# adjust the status of the menu entries related to the editor
|
||||
self.app.ui.menueditedit.setDisabled(False)
|
||||
self.app.ui.menueditok.setDisabled(True)
|
||||
|
@ -2741,14 +2756,17 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
self.app.ui.update_obj_btn.setEnabled(False)
|
||||
|
||||
self.app.ui.g_editor_cmenu.setEnabled(False)
|
||||
self.app.ui.grb_editor_cmenu.setEnabled(False)
|
||||
self.app.ui.e_editor_cmenu.setEnabled(False)
|
||||
|
||||
# adjust the visibility of some of the canvas context menu
|
||||
self.app.ui.popmenu_edit.setVisible(True)
|
||||
self.app.ui.popmenu_save.setVisible(False)
|
||||
|
||||
self.app.ui.popmenu_disable.setVisible(True)
|
||||
self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
|
||||
self.app.ui.popmenu_properties.setVisible(True)
|
||||
self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
|
||||
self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
||||
|
||||
# Show original geometry
|
||||
if self.gerber_obj:
|
||||
self.gerber_obj.visible = True
|
||||
|
@ -2771,6 +2789,20 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.canvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
|
||||
self.app.collection.view.clicked.disconnect()
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.disconnect()
|
||||
self.app.ui.popmenu_delete.triggered.disconnect()
|
||||
self.app.ui.popmenu_move.triggered.disconnect()
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(self.on_copy_button)
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
|
||||
self.app.ui.popmenu_move.triggered.connect(self.on_move_button)
|
||||
|
||||
# Gerber Editor
|
||||
self.app.ui.grb_draw_pad.triggered.connect(self.on_pad_add)
|
||||
self.app.ui.grb_draw_pad_array.triggered.connect(self.on_pad_add_array)
|
||||
self.app.ui.grb_draw_track.triggered.connect(self.on_track_add)
|
||||
self.app.ui.grb_draw_region.triggered.connect(self.on_region_add)
|
||||
|
||||
def disconnect_canvas_event_handlers(self):
|
||||
|
||||
# we restore the key and mouse control to FlatCAMApp method
|
||||
|
@ -2786,6 +2818,47 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
|
||||
self.canvas.vis_disconnect('mouse_release', self.on_grb_click_release)
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.popmenu_move.triggered.disconnect(self.on_move_button)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
|
||||
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
|
||||
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
|
||||
|
||||
# Gerber Editor
|
||||
|
||||
try:
|
||||
self.app.ui.grb_draw_pad.triggered.disconnect(self.on_pad_add)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.grb_draw_pad_array.triggered.disconnect(self.on_pad_add_array)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.grb_draw_track.triggered.disconnect(self.on_track_add)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.app.ui.grb_draw_region.triggered.disconnect(self.on_region_add)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def clear(self):
|
||||
self.active_tool = None
|
||||
# self.shape_buffer = []
|
||||
|
@ -3160,26 +3233,31 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
def on_canvas_click(self, event):
|
||||
"""
|
||||
event.x and .y have canvas coordinates
|
||||
event.xdaya and .ydata have plot coordinates
|
||||
event.xdata and .ydata have plot coordinates
|
||||
|
||||
:param event: Event object dispatched by Matplotlib
|
||||
:param event: Event object dispatched by VisPy
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
if self.app.grid_status():
|
||||
self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(self.pos[0], self.pos[1])]), symbol='++', edge_color='black',
|
||||
size=20)
|
||||
else:
|
||||
self.pos = (self.pos[0], self.pos[1])
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
if event.button is 1:
|
||||
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
||||
"%.4f " % (0, 0))
|
||||
self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
### Snap coordinates
|
||||
x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
||||
|
||||
self.pos = (x, y)
|
||||
|
||||
# Selection with left mouse button
|
||||
if self.active_tool is not None and event.button is 1:
|
||||
# Dispatch event to active_tool
|
||||
# msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
|
||||
msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
|
||||
|
||||
# If it is a shape generating tool
|
||||
|
@ -3213,10 +3291,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.app.log.debug("No active tool to respond to click!")
|
||||
|
||||
def on_grb_click_release(self, event):
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
self.modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
if self.app.grid_status():
|
||||
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
else:
|
||||
|
@ -3226,9 +3303,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
# canvas menu
|
||||
try:
|
||||
if event.button == 2: # right click
|
||||
if self.app.panning_action is True:
|
||||
self.app.panning_action = False
|
||||
else:
|
||||
if self.app.ui.popMenu.mouse_is_panning is False:
|
||||
if self.in_action is False:
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
|
@ -3243,6 +3318,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.select_tool('select')
|
||||
else:
|
||||
self.app.cursor = QtGui.QCursor()
|
||||
self.app.populate_cmenu_grids()
|
||||
self.app.ui.popMenu.popup(self.app.cursor.pos())
|
||||
else:
|
||||
# if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
||||
|
@ -3282,10 +3358,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.app.selection_type = None
|
||||
|
||||
elif isinstance(self.active_tool, FCApertureSelect):
|
||||
# Dispatch event to active_tool
|
||||
# msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
|
||||
# msg = self.active_tool.click_release((self.pos[0], self.pos[1]))
|
||||
# self.app.inform.emit(msg)
|
||||
self.active_tool.click_release((self.pos[0], self.pos[1]))
|
||||
|
||||
# if there are selected objects then plot them
|
||||
|
@ -3303,12 +3375,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
:type Bool
|
||||
:return:
|
||||
"""
|
||||
|
||||
poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
|
||||
sel_aperture = set()
|
||||
self.apertures_table.clearSelection()
|
||||
|
||||
self.app.delete_selection_shape()
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
for obj in self.storage_dict[storage]['solid_geometry']:
|
||||
if (sel_type is True and poly_selection.contains(obj.geo)) or \
|
||||
(sel_type is False and poly_selection.intersects(obj.geo)):
|
||||
|
@ -3322,7 +3396,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
else:
|
||||
self.selected.append(obj)
|
||||
sel_aperture.add(storage)
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
self.apertures_table.cellPressed.disconnect()
|
||||
except:
|
||||
|
@ -3349,22 +3424,18 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
:return: None
|
||||
"""
|
||||
|
||||
pos = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
event.xdata, event.ydata = pos[0], pos[1]
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
event.xdata, event.ydata = pos_canvas[0], pos_canvas[1]
|
||||
|
||||
self.x = event.xdata
|
||||
self.y = event.ydata
|
||||
|
||||
# Prevent updates on pan
|
||||
# if len(event.buttons) > 0:
|
||||
# return
|
||||
self.app.ui.popMenu.mouse_is_panning = False
|
||||
|
||||
# if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
||||
if event.button == 2:
|
||||
self.app.panning_action = True
|
||||
if event.button == 2 and event.is_dragging == 1:
|
||||
self.app.ui.popMenu.mouse_is_panning = True
|
||||
return
|
||||
else:
|
||||
self.app.panning_action = False
|
||||
|
||||
try:
|
||||
x = float(event.xdata)
|
||||
|
@ -3376,7 +3447,13 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
return
|
||||
|
||||
### Snap coordinates
|
||||
x, y = self.app.geo_editor.app.geo_editor.snap(x, y)
|
||||
if self.app.grid_status():
|
||||
x, y = self.app.geo_editor.snap(x, y)
|
||||
self.app.app_cursor.enabled = True
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
|
||||
else:
|
||||
self.app.app_cursor.enabled = False
|
||||
|
||||
self.snap_x = x
|
||||
self.snap_y = y
|
||||
|
@ -3398,7 +3475,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
geo = self.active_tool.utility_geometry(data=(x, y))
|
||||
|
||||
if isinstance(geo, DrawToolShape) and geo.geo is not None:
|
||||
|
||||
# Remove any previous utility shape
|
||||
self.tool_shape.clear(update=True)
|
||||
self.draw_utility_geometry(geo=geo)
|
||||
|
@ -3410,7 +3486,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
if isinstance(self.active_tool, FCRegion) or isinstance(self.active_tool, FCTrack):
|
||||
pass
|
||||
else:
|
||||
dx = pos[0] - self.pos[0]
|
||||
dx = pos_canvas[0] - self.pos[0]
|
||||
self.app.delete_selection_shape()
|
||||
if dx < 0:
|
||||
self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y),
|
||||
|
@ -3423,9 +3499,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
else:
|
||||
self.app.selection_type = None
|
||||
|
||||
# Update cursor
|
||||
self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
|
||||
|
||||
def on_canvas_key_release(self, event):
|
||||
self.key = None
|
||||
|
||||
|
@ -3459,6 +3532,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.shapes.clear(update=True)
|
||||
|
||||
for storage in self.storage_dict:
|
||||
try:
|
||||
for shape in self.storage_dict[storage]['solid_geometry']:
|
||||
if shape.geo is None:
|
||||
continue
|
||||
|
@ -3468,6 +3542,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
linewidth=2)
|
||||
continue
|
||||
self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for shape in self.utility:
|
||||
self.plot_shape(geometry=shape.geo, linewidth=1)
|
||||
|
@ -3498,6 +3574,13 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.shapes.add(shape=geometry, color=color, face_color=color+'AF', layer=0)
|
||||
|
||||
def start_delayed_plot(self, check_period):
|
||||
"""
|
||||
This function starts an QTImer and it will periodically check if all the workers finish the plotting functions
|
||||
|
||||
:param check_period: time at which to check periodically if all plots finished to be plotted
|
||||
:return:
|
||||
"""
|
||||
|
||||
# self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
|
||||
# self.plot_thread.start()
|
||||
log.debug("FlatCAMGrbEditor --> Delayed Plot started.")
|
||||
|
@ -3507,7 +3590,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.plot_thread.start()
|
||||
|
||||
def check_plot_finished(self):
|
||||
# print(self.grb_plot_promises)
|
||||
"""
|
||||
If all the promises made are finished then all the shapes are in shapes_storage and can be plotted safely and
|
||||
then the UI is rebuilt accordingly.
|
||||
:return:
|
||||
"""
|
||||
|
||||
try:
|
||||
if not self.grb_plot_promises:
|
||||
self.plot_thread.stop()
|
||||
|
@ -3571,13 +3659,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
return
|
||||
|
||||
for storage in self.storage_dict:
|
||||
# try:
|
||||
# self.storage_dict[storage].remove(shape)
|
||||
# except:
|
||||
# pass
|
||||
try:
|
||||
if shape in self.storage_dict[storage]['solid_geometry']:
|
||||
self.storage_dict[storage]['solid_geometry'].remove(shape)
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
if shape in self.selected:
|
||||
self.selected.remove(shape) # TODO: Check performance
|
||||
|
||||
|
|
|
@ -638,6 +638,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.panelize_btn = self.toolbartools.addAction(QtGui.QIcon('share/panel16.png'), _("Panel Tool"))
|
||||
self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'),_( "Film Tool"))
|
||||
self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool"))
|
||||
self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool"))
|
||||
|
||||
self.toolbartools.addSeparator()
|
||||
|
||||
self.calculators_btn = self.toolbartools.addAction(QtGui.QIcon('share/calculator24.png'), _("Calculators Tool"))
|
||||
|
@ -699,7 +701,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/poligonize32.png'),
|
||||
_("Poligonize"))
|
||||
|
||||
|
||||
self.grb_add_semidisc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/semidisc32.png'), _("SemiDisc"))
|
||||
self.grb_add_disc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/disc32.png'), _("Disc"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
|
@ -752,12 +753,27 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
################
|
||||
|
||||
### Project ###
|
||||
# self.project_tab = QtWidgets.QWidget()
|
||||
# self.project_tab.setObjectName("project_tab")
|
||||
# # project_tab.setMinimumWidth(250) # Hack
|
||||
# self.project_tab_layout = QtWidgets.QVBoxLayout(self.project_tab)
|
||||
# self.project_tab_layout.setContentsMargins(2, 2, 2, 2)
|
||||
# self.notebook.addTab(self.project_tab,_( "Project"))
|
||||
|
||||
self.project_tab = QtWidgets.QWidget()
|
||||
self.project_tab.setObjectName("project_tab")
|
||||
# project_tab.setMinimumWidth(250) # Hack
|
||||
self.project_tab_layout = QtWidgets.QVBoxLayout(self.project_tab)
|
||||
|
||||
self.project_frame_lay = QtWidgets.QVBoxLayout(self.project_tab)
|
||||
self.project_frame_lay.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.project_frame = QtWidgets.QFrame()
|
||||
self.project_frame.setContentsMargins(0, 0, 0, 0)
|
||||
self.project_frame_lay.addWidget(self.project_frame)
|
||||
|
||||
self.project_tab_layout = QtWidgets.QVBoxLayout(self.project_frame)
|
||||
self.project_tab_layout.setContentsMargins(2, 2, 2, 2)
|
||||
self.notebook.addTab(self.project_tab,_( "Project"))
|
||||
self.notebook.addTab(self.project_tab, _("Project"))
|
||||
self.project_frame.setDisabled(False)
|
||||
|
||||
### Selected ###
|
||||
self.selected_tab = QtWidgets.QWidget()
|
||||
|
@ -1545,12 +1561,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
##############################################################
|
||||
### HERE WE BUILD THE CONTEXT MENU FOR RMB CLICK ON CANVAS ###
|
||||
##############################################################
|
||||
self.popMenu = QtWidgets.QMenu()
|
||||
self.popMenu = FCMenu()
|
||||
|
||||
self.popmenu_disable = self.popMenu.addAction(QtGui.QIcon('share/clear_plot32.png'), _("Disable"))
|
||||
self.popMenu.addSeparator()
|
||||
self.cmenu_newmenu = self.popMenu.addMenu(QtGui.QIcon('share/file32.png'), _("New"))
|
||||
self.popmenu_new_geo = self.cmenu_newmenu.addAction(QtGui.QIcon('share/new_geo32_bis.png'), _("Geometry"))
|
||||
self.popmenu_new_grb = self.cmenu_newmenu.addAction(QtGui.QIcon('share/flatcam_icon32.png'), "Gerber")
|
||||
self.popmenu_new_exc = self.cmenu_newmenu.addAction(QtGui.QIcon('share/new_exc32.png'), _("Excellon"))
|
||||
self.cmenu_newmenu.addSeparator()
|
||||
self.popmenu_new_prj = self.cmenu_newmenu.addAction(QtGui.QIcon('share/file16.png'), _("Project"))
|
||||
|
@ -1576,15 +1593,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.grb_draw_pad_array = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/padarray32.png'), _("Pad Array"))
|
||||
self.grb_draw_track = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/track32.png'), _("Track"))
|
||||
self.grb_draw_region = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/polygon32.png'), _("Region"))
|
||||
self.grb_editor_cmenu.addSeparator()
|
||||
self.grb_copy = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/copy.png'), _("Copy"))
|
||||
self.grb_delete = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/trash32.png'), _("Delete"))
|
||||
self.grb_move = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
|
||||
|
||||
self.e_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/drill32.png'), _("Exc Editor"))
|
||||
self.drill = self.e_editor_cmenu.addAction(QtGui.QIcon('share/drill32.png'), _("Add Drill"))
|
||||
self.drill_array = self.e_editor_cmenu.addAction(QtGui.QIcon('share/addarray32.png'), _("Add Drill Array"))
|
||||
self.drill_copy = self.e_editor_cmenu.addAction(QtGui.QIcon('share/copy32.png'), _("Copy Drill(s)"))
|
||||
|
||||
self.popMenu.addSeparator()
|
||||
self.popmenu_copy = self.popMenu.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
|
||||
|
@ -1724,9 +1736,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# start with GRID activated
|
||||
self.grid_snap_btn.trigger()
|
||||
|
||||
self.g_editor_cmenu.setEnabled(False)
|
||||
self.grb_editor_cmenu.setEnabled(False)
|
||||
self.e_editor_cmenu.setEnabled(False)
|
||||
self.g_editor_cmenu.menuAction().setVisible(False)
|
||||
self.grb_editor_cmenu.menuAction().setVisible(False)
|
||||
self.e_editor_cmenu.menuAction().setVisible(False)
|
||||
|
||||
self.general_defaults_form = GeneralPreferencesUI()
|
||||
self.gerber_defaults_form = GerberPreferencesUI()
|
||||
|
@ -1844,13 +1856,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool"))
|
||||
self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'),
|
||||
_("SolderPaste Tool"))
|
||||
self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool"))
|
||||
|
||||
self.toolbartools.addSeparator()
|
||||
|
||||
self.calculators_btn = self.toolbartools.addAction(QtGui.QIcon('share/calculator24.png'),
|
||||
_("Calculators Tool"))
|
||||
self.transform_btn = self.toolbartools.addAction(QtGui.QIcon('share/transform.png'), _("Transform Tool"))
|
||||
|
||||
### Drill Editor Toolbar ###
|
||||
### Excellon Editor Toolbar ###
|
||||
self.select_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
|
||||
self.add_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/plus16.png'), _('Add Drill Hole'))
|
||||
self.add_drill_array_btn = self.exc_edit_toolbar.addAction(
|
||||
|
@ -1906,6 +1920,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
|
||||
self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
|
||||
self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
|
||||
self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/poligonize32.png'),
|
||||
_("Poligonize"))
|
||||
|
||||
self.grb_add_semidisc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/semidisc32.png'), _("SemiDisc"))
|
||||
self.grb_add_disc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/disc32.png'), _("Disc"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
|
||||
self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
|
||||
|
@ -1974,7 +1993,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setVisible(True)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setVisible(False)
|
||||
self.grb_edit_toolbar.setVisible(True)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.corner_snap_btn.setVisible(True)
|
||||
|
@ -2146,6 +2165,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.cutout_tool.run(toggle=True)
|
||||
return
|
||||
|
||||
# Substract Tool
|
||||
if key == QtCore.Qt.Key_W:
|
||||
self.app.sub_tool.run(toggle=True)
|
||||
return
|
||||
|
||||
# Panelize Tool
|
||||
if key == QtCore.Qt.Key_Z:
|
||||
self.app.panelize_tool.run(toggle=True)
|
||||
|
@ -2214,6 +2238,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_Space:
|
||||
for select in selected:
|
||||
select.ui.plot_cb.toggle()
|
||||
self.app.collection.update_view()
|
||||
self.app.delete_selection_shape()
|
||||
|
||||
# New Geometry
|
||||
|
@ -2807,7 +2832,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.exc_editor.replot()
|
||||
# self.select_btn.setChecked(True)
|
||||
# self.on_tool_select('select')
|
||||
self.app.exc_editor.select_tool('select')
|
||||
self.app.exc_editor.select_tool('drill_select')
|
||||
return
|
||||
|
||||
# Delete selected object if delete key event comes out of canvas
|
||||
|
@ -2943,7 +2968,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_T or key == 'T':
|
||||
self.app.exc_editor.launched_from_shortcuts = True
|
||||
## Current application units in Upper Case
|
||||
self.units = self.general_defaults_group.general_app_group.units_radio.get_value().upper()
|
||||
self.units = self.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
tool_add_popup = FCInputDialog(title=_("New Tool ..."),
|
||||
text=_('Enter a Tool Diameter:'),
|
||||
min=0.0000, max=99.9999, decimals=4)
|
||||
|
@ -3047,6 +3072,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
'params': [self.filename, object_type, None]})
|
||||
|
||||
if extension in self.app.pdf_list:
|
||||
self.app.pdf_tool.periodic_check(1000)
|
||||
self.app.worker_task.emit({'fcn': self.app.pdf_tool.open_pdf,
|
||||
'params': [self.filename]})
|
||||
|
||||
|
@ -3483,6 +3509,34 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||
self.form_box_child_11.addWidget(self.sel_draw_color_button)
|
||||
self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||
|
||||
# Project Tab items color
|
||||
self.proj_color_label = QtWidgets.QLabel(_('Project Items:'))
|
||||
self.proj_color_label.setToolTip(
|
||||
_("Set the color of the items in Project Tab Tree.")
|
||||
)
|
||||
self.proj_color_entry = FCEntry()
|
||||
self.proj_color_button = QtWidgets.QPushButton()
|
||||
self.proj_color_button.setFixedSize(15, 15)
|
||||
|
||||
self.form_box_child_12 = QtWidgets.QHBoxLayout()
|
||||
self.form_box_child_12.addWidget(self.proj_color_entry)
|
||||
self.form_box_child_12.addWidget(self.proj_color_button)
|
||||
self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||
|
||||
self.proj_color_dis_label = QtWidgets.QLabel(_('Proj. Dis. Items:'))
|
||||
self.proj_color_dis_label.setToolTip(
|
||||
_("Set the color of the items in Project Tab Tree,\n"
|
||||
"for the case when the items are disabled.")
|
||||
)
|
||||
self.proj_color_dis_entry = FCEntry()
|
||||
self.proj_color_dis_button = QtWidgets.QPushButton()
|
||||
self.proj_color_dis_button.setFixedSize(15, 15)
|
||||
|
||||
self.form_box_child_13 = QtWidgets.QHBoxLayout()
|
||||
self.form_box_child_13.addWidget(self.proj_color_dis_entry)
|
||||
self.form_box_child_13.addWidget(self.proj_color_dis_button)
|
||||
self.form_box_child_13.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||
|
||||
# Just to add empty rows
|
||||
self.spacelabel = QtWidgets.QLabel('')
|
||||
|
||||
|
@ -3507,6 +3561,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
|
|||
self.form_box.addRow(self.alt_sl_color_label, self.form_box_child_9)
|
||||
self.form_box.addRow(self.draw_color_label, self.form_box_child_10)
|
||||
self.form_box.addRow(self.sel_draw_color_label, self.form_box_child_11)
|
||||
self.form_box.addRow(QtWidgets.QLabel(""))
|
||||
self.form_box.addRow(self.proj_color_label, self.form_box_child_12)
|
||||
self.form_box.addRow(self.proj_color_dis_label, self.form_box_child_13)
|
||||
|
||||
self.form_box.addRow(self.spacelabel, self.spacelabel)
|
||||
|
||||
|
@ -3589,6 +3646,16 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
|
|||
)
|
||||
self.hover_cb = FCCheckBox()
|
||||
|
||||
# Enable Selection box
|
||||
self.selection_label = QtWidgets.QLabel(_('Sel. Shape:'))
|
||||
self.selection_label.setToolTip(
|
||||
_("Enable the display of a selection shape for FlatCAM objects.\n"
|
||||
"It is displayed whenever the mouse selects an object\n"
|
||||
"either by clicking or dragging mouse from left to right or\n"
|
||||
"right to left.")
|
||||
)
|
||||
self.selection_cb = FCCheckBox()
|
||||
|
||||
# Just to add empty rows
|
||||
self.spacelabel = QtWidgets.QLabel('')
|
||||
|
||||
|
@ -3600,6 +3667,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
|
|||
self.form_box.addRow(self.hdpi_label, self.hdpi_cb)
|
||||
self.form_box.addRow(self.clear_label, self.clear_btn)
|
||||
self.form_box.addRow(self.hover_label, self.hover_cb)
|
||||
self.form_box.addRow(self.selection_label, self.selection_cb)
|
||||
|
||||
# Add the QFormLayout that holds the Application general defaults
|
||||
# to the main layout of this TAB
|
||||
|
@ -3667,8 +3735,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
"ADVANCED level -> full functionality.\n\n"
|
||||
"The choice here will influence the parameters in\n"
|
||||
"the Selected Tab for all kinds of FlatCAM objects."))
|
||||
self.app_level_radio = RadioSet([{'label': _('Basic'), 'value': 'b'},
|
||||
{'label': _('Advanced'), 'value': 'a'}])
|
||||
self.app_level_radio = RadioSet([{'label': 'Basic', 'value': 'b'},
|
||||
{'label': 'Advanced', 'value': 'a'}])
|
||||
|
||||
# Languages for FlatCAM
|
||||
self.languagelabel = QtWidgets.QLabel(_('<b>Languages:</b>'))
|
||||
|
@ -3676,6 +3744,13 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
self.language_cb = FCComboBox()
|
||||
self.languagespace = QtWidgets.QLabel('')
|
||||
self.language_apply_btn = FCButton(_("Apply Language"))
|
||||
self.language_apply_btn.setToolTip(_("Set the language used throughout FlatCAM.\n"
|
||||
"The app will restart after click."
|
||||
"Windows: When FlatCAM is installed in Program Files\n"
|
||||
"directory, it is possible that the app will not\n"
|
||||
"restart after the button is clicked due of Windows\n"
|
||||
"security features. In this case the language will be\n"
|
||||
"applied at the next app start."))
|
||||
|
||||
# Shell StartUp CB
|
||||
self.shell_startup_label = QtWidgets.QLabel(_('Shell at StartUp:'))
|
||||
|
@ -3720,14 +3795,14 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
self.panbuttonlabel.setToolTip(_("Select the mouse button to use for panning:\n"
|
||||
"- MMB --> Middle Mouse Button\n"
|
||||
"- RMB --> Right Mouse Button"))
|
||||
self.pan_button_radio = RadioSet([{'label': _('MMB'), 'value': '3'},
|
||||
{'label': _('RMB'), 'value': '2'}])
|
||||
self.pan_button_radio = RadioSet([{'label': 'MMB', 'value': '3'},
|
||||
{'label': 'RMB', 'value': '2'}])
|
||||
|
||||
# Multiple Selection Modifier Key
|
||||
self.mselectlabel = QtWidgets.QLabel(_('<b>Multiple Sel:</b>'))
|
||||
self.mselectlabel.setToolTip(_("Select the key used for multiple selection."))
|
||||
self.mselect_radio = RadioSet([{'label': _('CTRL'), 'value': 'Control'},
|
||||
{'label': _('SHIFT'), 'value': 'Shift'}])
|
||||
self.mselect_radio = RadioSet([{'label': 'CTRL', 'value': 'Control'},
|
||||
{'label': 'SHIFT', 'value': 'Shift'}])
|
||||
|
||||
# Project at StartUp CB
|
||||
self.project_startup_label = QtWidgets.QLabel(_('Project at StartUp:'))
|
||||
|
@ -3955,8 +4030,8 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
|
|||
"- conventional / useful when there is no backlash compensation")
|
||||
)
|
||||
grid0.addWidget(milling_type_label, 3, 0)
|
||||
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||
{'label': _('Conv.'), 'value': 'cv'}])
|
||||
self.milling_type_radio = RadioSet([{'label': 'Climb', 'value': 'cl'},
|
||||
{'label': 'Conv.', 'value': 'cv'}])
|
||||
grid0.addWidget(self.milling_type_radio, 3, 1)
|
||||
|
||||
# Combine passes
|
||||
|
@ -4224,8 +4299,8 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
hlay3.addWidget(self.excellon_zeros_label)
|
||||
|
||||
self.excellon_zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
|
||||
{'label': _('TZ'), 'value': 'T'}])
|
||||
self.excellon_zeros_radio = RadioSet([{'label': 'LZ', 'value': 'L'},
|
||||
{'label': 'TZ', 'value': 'T'}])
|
||||
self.excellon_zeros_radio.setToolTip(
|
||||
_("This sets the default type of Excellon zeros.\n"
|
||||
"If it is not detected in the parsed file the value here\n"
|
||||
|
@ -4252,8 +4327,8 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
hlay4.addWidget(self.excellon_units_label)
|
||||
|
||||
self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
|
||||
{'label': _('MM'), 'value': 'METRIC'}])
|
||||
self.excellon_units_radio = RadioSet([{'label': 'INCH', 'value': 'INCH'},
|
||||
{'label': 'MM', 'value': 'METRIC'}])
|
||||
self.excellon_units_radio.setToolTip(
|
||||
_("This sets the units of Excellon files.\n"
|
||||
"Some Excellon files don't have an header\n"
|
||||
|
@ -4291,8 +4366,8 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
|
|||
"Travelling Salesman algorithm for path optimization.")
|
||||
)
|
||||
|
||||
self.excellon_optimization_radio = RadioSet([{'label': _('MH'), 'value': 'M'},
|
||||
{'label': _('Basic'), 'value': 'B'}])
|
||||
self.excellon_optimization_radio = RadioSet([{'label': 'MH', 'value': 'M'},
|
||||
{'label': 'Basic', 'value': 'B'}])
|
||||
self.excellon_optimization_radio.setToolTip(
|
||||
_("This sets the optimization type for the Excellon drill path.\n"
|
||||
"If MH is checked then Google OR-Tools algorithm with MetaHeuristic\n"
|
||||
|
@ -4457,9 +4532,9 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
"When choosing 'Slots' or 'Both', slots will be\n"
|
||||
"converted to drills.")
|
||||
)
|
||||
self.excellon_gcode_type_radio = RadioSet([{'label': _('Drills'), 'value': 'drills'},
|
||||
{'label': _('Slots'), 'value': 'slots'},
|
||||
{'label': _('Both'), 'value': 'both'}])
|
||||
self.excellon_gcode_type_radio = RadioSet([{'label': 'Drills', 'value': 'drills'},
|
||||
{'label': 'Slots', 'value': 'slots'},
|
||||
{'label': 'Both', 'value': 'both'}])
|
||||
grid2.addWidget(excellon_gcode_type_label, 9, 0)
|
||||
grid2.addWidget(self.excellon_gcode_type_radio, 9, 1)
|
||||
|
||||
|
@ -4643,8 +4718,8 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
|
|||
_("The units used in the Excellon file.")
|
||||
)
|
||||
|
||||
self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
|
||||
{'label': _('MM'), 'value': 'METRIC'}])
|
||||
self.excellon_units_radio = RadioSet([{'label': 'INCH', 'value': 'INCH'},
|
||||
{'label': 'MM', 'value': 'METRIC'}])
|
||||
self.excellon_units_radio.setToolTip(
|
||||
_("The units used in the Excellon file.")
|
||||
)
|
||||
|
@ -4699,8 +4774,8 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
|
|||
"Also it will have to be specified if LZ = leading zeros are kept\n"
|
||||
"or TZ = trailing zeros are kept.")
|
||||
)
|
||||
self.format_radio = RadioSet([{'label': _('Decimal'), 'value': 'dec'},
|
||||
{'label': _('No-Decimal'), 'value': 'ndec'}])
|
||||
self.format_radio = RadioSet([{'label': 'Decimal', 'value': 'dec'},
|
||||
{'label': 'No-Decimal', 'value': 'ndec'}])
|
||||
self.format_radio.setToolTip(
|
||||
_("Select the kind of coordinates format used.\n"
|
||||
"Coordinates can be saved with decimal point or without.\n"
|
||||
|
@ -4723,8 +4798,8 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
|
|||
"and Leading Zeros are removed.")
|
||||
)
|
||||
|
||||
self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'},
|
||||
{'label': _('TZ'), 'value': 'TZ'}])
|
||||
self.zeros_radio = RadioSet([{'label': 'LZ', 'value': 'LZ'},
|
||||
{'label': 'TZ', 'value': 'TZ'}])
|
||||
self.zeros_radio.setToolTip(
|
||||
_("This sets the default type of Excellon zeros.\n"
|
||||
"If LZ then Leading Zeros are kept and\n"
|
||||
|
@ -5105,9 +5180,9 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
|
||||
self.cncplot_method_radio = RadioSet([
|
||||
{"label": _("All"), "value": "all"},
|
||||
{"label": _("Travel"), "value": "travel"},
|
||||
{"label": _("Cut"), "value": "cut"}
|
||||
{"label": "All", "value": "all"},
|
||||
{"label": "Travel", "value": "travel"},
|
||||
{"label": "Cut", "value": "cut"}
|
||||
], stretch=False)
|
||||
|
||||
grid0.addWidget(self.cncplot_method_label, 1, 0)
|
||||
|
@ -5341,9 +5416,9 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(methodlabel, 3, 0)
|
||||
self.ncc_method_radio = RadioSet([
|
||||
{"label": _("Standard"), "value": "standard"},
|
||||
{"label": _("Seed-based"), "value": "seed"},
|
||||
{"label": _("Straight lines"), "value": "lines"}
|
||||
{"label": "Standard", "value": "standard"},
|
||||
{"label": "Seed-based", "value": "seed"},
|
||||
{"label": "Straight lines", "value": "lines"}
|
||||
], orientation='vertical', stretch=False)
|
||||
grid0.addWidget(self.ncc_method_radio, 3, 1)
|
||||
|
||||
|
@ -5490,8 +5565,8 @@ class Tools2sidedPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.drill_dia_entry, 0, 1)
|
||||
|
||||
## Axis
|
||||
self.mirror_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
|
||||
{'label': _('Y'), 'value': 'Y'}])
|
||||
self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
|
||||
{'label': 'Y', 'value': 'Y'}])
|
||||
self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
|
||||
self.mirax_label.setToolTip(
|
||||
_("Mirror vertically (X) or horizontally (Y).")
|
||||
|
@ -5503,8 +5578,8 @@ class Tools2sidedPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.mirror_axis_radio, 2, 1)
|
||||
|
||||
## Axis Location
|
||||
self.axis_location_radio = RadioSet([{'label': _('Point'), 'value': 'point'},
|
||||
{'label': _('Box'), 'value': 'box'}])
|
||||
self.axis_location_radio = RadioSet([{'label': 'Point', 'value': 'point'},
|
||||
{'label': 'Box', 'value': 'box'}])
|
||||
self.axloc_label = QtWidgets.QLabel(_("Axis Ref:"))
|
||||
self.axloc_label.setToolTip(
|
||||
_("The axis should pass through a <b>point</b> or cut\n "
|
||||
|
@ -5581,9 +5656,9 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(methodlabel, 3, 0)
|
||||
self.paintmethod_combo = RadioSet([
|
||||
{"label": _("Standard"), "value": "standard"},
|
||||
{"label": _("Seed-based"), "value": "seed"},
|
||||
{"label": _("Straight lines"), "value": "lines"}
|
||||
{"label": "Standard", "value": "standard"},
|
||||
{"label": "Seed-based", "value": "seed"},
|
||||
{"label": "Straight lines", "value": "lines"}
|
||||
], orientation='vertical', stretch=False)
|
||||
grid0.addWidget(self.paintmethod_combo, 3, 1)
|
||||
|
||||
|
@ -5614,8 +5689,8 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(selectlabel, 6, 0)
|
||||
self.selectmethod_combo = RadioSet([
|
||||
{"label": _("Single"), "value": "single"},
|
||||
{"label": _("All"), "value": "all"},
|
||||
{"label": "Single", "value": "single"},
|
||||
{"label": "All", "value": "all"},
|
||||
# {"label": "Rectangle", "value": "rectangle"}
|
||||
])
|
||||
grid0.addWidget(self.selectmethod_combo, 6, 1)
|
||||
|
@ -5642,8 +5717,8 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
|
|||
grid0 = QtWidgets.QGridLayout()
|
||||
self.layout.addLayout(grid0)
|
||||
|
||||
self.film_type_radio = RadioSet([{'label': _('Pos'), 'value': 'pos'},
|
||||
{'label': _('Neg'), 'value': 'neg'}])
|
||||
self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
|
||||
{'label': 'Neg', 'value': 'neg'}])
|
||||
ftypelbl = QtWidgets.QLabel(_('Film Type:'))
|
||||
ftypelbl.setToolTip(
|
||||
_("Generate a Positive black film or a Negative film.\n"
|
||||
|
@ -5742,8 +5817,8 @@ class ToolsPanelizePrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.prows, 3, 1)
|
||||
|
||||
## Type of resulting Panel object
|
||||
self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
|
||||
{'label': _('Geo'), 'value': 'geometry'}])
|
||||
self.panel_type_radio = RadioSet([{'label': 'Gerber', 'value': 'gerber'},
|
||||
{'label': 'Geo', 'value': 'geometry'}])
|
||||
self.panel_type_label = QtWidgets.QLabel(_("Panel Type:"))
|
||||
self.panel_type_label.setToolTip(
|
||||
_( "Choose the type of object for the panel object:\n"
|
||||
|
|
|
@ -367,7 +367,11 @@ class FCEntry2(FCEntry):
|
|||
self.readyToEdit = True
|
||||
|
||||
def set_value(self, val):
|
||||
self.setText('%.4f' % float(val))
|
||||
try:
|
||||
fval = float(val)
|
||||
except ValueError:
|
||||
return
|
||||
self.setText('%.4f' % fval)
|
||||
|
||||
|
||||
class EvalEntry(QtWidgets.QLineEdit):
|
||||
|
@ -676,6 +680,16 @@ class FCButton(QtWidgets.QPushButton):
|
|||
self.setText(str(val))
|
||||
|
||||
|
||||
class FCMenu(QtWidgets.QMenu):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.mouse_is_panning = False
|
||||
|
||||
def popup(self, pos, action=None):
|
||||
self.mouse_is_panning = False
|
||||
super().popup(pos)
|
||||
|
||||
|
||||
class FCTab(QtWidgets.QTabWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(FCTab, self).__init__(parent)
|
||||
|
|
|
@ -153,7 +153,7 @@ class GerberObjectUI(ObjectUI):
|
|||
grid0.addWidget(self.plot_options_label, 0, 0)
|
||||
|
||||
# Solid CB
|
||||
self.solid_cb = FCCheckBox(label=_('Solid '))
|
||||
self.solid_cb = FCCheckBox(label=_('Solid'))
|
||||
self.solid_cb.setToolTip(
|
||||
_("Solid color polygons.")
|
||||
)
|
||||
|
@ -161,7 +161,7 @@ class GerberObjectUI(ObjectUI):
|
|||
grid0.addWidget(self.solid_cb, 0, 1)
|
||||
|
||||
# Multicolored CB
|
||||
self.multicolored_cb = FCCheckBox(label=_('M-Color '))
|
||||
self.multicolored_cb = FCCheckBox(label=_('M-Color'))
|
||||
self.multicolored_cb.setToolTip(
|
||||
_("Draw polygons in different colors.")
|
||||
)
|
||||
|
@ -299,8 +299,8 @@ class GerberObjectUI(ObjectUI):
|
|||
"- conventional / useful when there is no backlash compensation")
|
||||
)
|
||||
grid1.addWidget(self.milling_type_label, 3, 0)
|
||||
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||
{'label': _('Conv.'), 'value': 'cv'}])
|
||||
self.milling_type_radio = RadioSet([{'label': 'Climb', 'value': 'cl'},
|
||||
{'label': 'Conv.', 'value': 'cv'}])
|
||||
grid1.addWidget(self.milling_type_radio, 3, 1)
|
||||
|
||||
# combine all passes CB
|
||||
|
@ -749,9 +749,9 @@ class ExcellonObjectUI(ObjectUI):
|
|||
"When choosing 'Slots' or 'Both', slots will be\n"
|
||||
"converted to a series of drills.")
|
||||
)
|
||||
self.excellon_gcode_type_radio = RadioSet([{'label': _('Drills'), 'value': 'drills'},
|
||||
{'label': _('Slots'), 'value': 'slots'},
|
||||
{'label': _('Both'), 'value': 'both'}])
|
||||
self.excellon_gcode_type_radio = RadioSet([{'label': 'Drills', 'value': 'drills'},
|
||||
{'label': 'Slots', 'value': 'slots'},
|
||||
{'label': 'Both', 'value': 'both'}])
|
||||
gcode_box.addRow(gcode_type_label, self.excellon_gcode_type_radio)
|
||||
self.tools_box.addLayout(gcode_box)
|
||||
|
||||
|
@ -1355,9 +1355,9 @@ class CNCObjectUI(ObjectUI):
|
|||
)
|
||||
|
||||
self.cncplot_method_combo = RadioSet([
|
||||
{"label": _("All"), "value": "all"},
|
||||
{"label": _("Travel"), "value": "travel"},
|
||||
{"label": _("Cut"), "value": "cut"}
|
||||
{"label": "All", "value": "all"},
|
||||
{"label": "Travel", "value": "travel"},
|
||||
{"label": "Cut", "value": "cut"}
|
||||
], stretch=False)
|
||||
|
||||
## Object name
|
||||
|
|
|
@ -12,9 +12,9 @@ import math
|
|||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
import builtins
|
||||
|
||||
fcTranslate.apply_language('strings')
|
||||
import builtins
|
||||
if '_' not in builtins.__dict__:
|
||||
_ = gettext.gettext
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://flatcam.org #
|
||||
# File Author: Marius Adrian Stanciu (c) #
|
||||
# Date: 3/10/2019 #
|
||||
# Date: 4/23/2019 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
|
@ -18,6 +18,7 @@ import numpy as np
|
|||
|
||||
import zlib
|
||||
import re
|
||||
import time
|
||||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
|
@ -106,9 +107,18 @@ class ToolPDF(FlatCAMTool):
|
|||
self.gs['transform'] = []
|
||||
self.gs['line_width'] = [] # each element is a float
|
||||
|
||||
self.obj_dict = dict()
|
||||
self.pdf_parsed = ''
|
||||
self.parsed_obj_dict = dict()
|
||||
self.pdf_decompressed = {}
|
||||
|
||||
# key = file name and extension
|
||||
# value is a dict to store the parsed content of the PDF
|
||||
self.pdf_parsed = {}
|
||||
|
||||
# QTimer for periodic check
|
||||
self.check_thread = QtCore.QTimer()
|
||||
|
||||
# Every time a parser is started we add a promise; every time a parser finished we remove a promise
|
||||
# when empty we start the layer rendering
|
||||
self.parsing_promises = []
|
||||
|
||||
# conversion factor to INCH
|
||||
self.point_to_unit_factor = 0.01388888888
|
||||
|
@ -148,16 +158,22 @@ class ToolPDF(FlatCAMTool):
|
|||
if len(filenames) == 0:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Open PDF cancelled."))
|
||||
else:
|
||||
# start the parsing timer with a period of 1 second
|
||||
self.periodic_check(1000)
|
||||
|
||||
for filename in filenames:
|
||||
if filename != '':
|
||||
self.app.worker_task.emit({'fcn': self.open_pdf, 'params': [filename]})
|
||||
self.app.worker_task.emit({'fcn': self.open_pdf,
|
||||
'params': [filename]})
|
||||
|
||||
def open_pdf(self, filename):
|
||||
new_name = filename.split('/')[-1].split('\\')[-1]
|
||||
self.obj_dict.clear()
|
||||
self.pdf_parsed = ''
|
||||
self.parsed_obj_dict = {}
|
||||
obj_type = 'gerber'
|
||||
short_name = filename.split('/')[-1].split('\\')[-1]
|
||||
self.parsing_promises.append(short_name)
|
||||
self.pdf_parsed[short_name] = {}
|
||||
self.pdf_parsed[short_name]['pdf'] = {}
|
||||
self.pdf_parsed[short_name]['filename'] = filename
|
||||
|
||||
self.pdf_decompressed[short_name] = ''
|
||||
|
||||
# the UNITS in PDF files are points and here we set the factor to convert them to real units (either MM or INCH)
|
||||
if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() == 'MM':
|
||||
|
@ -177,27 +193,34 @@ class ToolPDF(FlatCAMTool):
|
|||
log.debug(" PDF STREAM: %d\n" % stream_nr)
|
||||
s = s.strip(b'\r\n')
|
||||
try:
|
||||
self.pdf_parsed += (zlib.decompress(s).decode('UTF-8') + '\r\n')
|
||||
self.pdf_decompressed[short_name] += (zlib.decompress(s).decode('UTF-8') + '\r\n')
|
||||
except Exception as e:
|
||||
log.debug("ToolPDF.open_pdf().obj_init() --> %s" % str(e))
|
||||
|
||||
self.parsed_obj_dict = self.parse_pdf(pdf_content=self.pdf_parsed)
|
||||
self.pdf_parsed[short_name]['pdf'] = self.parse_pdf(pdf_content=self.pdf_decompressed[short_name])
|
||||
# we used it, now we delete it
|
||||
self.pdf_decompressed[short_name] = ''
|
||||
|
||||
for k in self.parsed_obj_dict:
|
||||
ap_dict = deepcopy(self.parsed_obj_dict[k])
|
||||
if ap_dict:
|
||||
if k == 0:
|
||||
# Excellon
|
||||
obj_type = 'excellon'
|
||||
# removal from list is done in a multithreaded way therefore not always the removal can be done
|
||||
# try to remove until it's done
|
||||
try:
|
||||
while True:
|
||||
self.parsing_promises.remove(short_name)
|
||||
time.sleep(0.1)
|
||||
except:
|
||||
pass
|
||||
self.app.inform.emit(_("[success] Opened: %s") % filename)
|
||||
|
||||
new_name = new_name + "_exc"
|
||||
# store the points here until reconstitution: keys are diameters and values are list of (x,y) coords
|
||||
def layer_rendering_as_excellon(self, filename, ap_dict, layer_nr):
|
||||
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
|
||||
|
||||
# store the points here until reconstitution:
|
||||
# keys are diameters and values are list of (x,y) coords
|
||||
points = {}
|
||||
|
||||
def obj_init(exc_obj, app_obj):
|
||||
# print(self.parsed_obj_dict[0])
|
||||
|
||||
for geo in self.parsed_obj_dict[0]['0']['solid_geometry']:
|
||||
for geo in ap_dict['0']['solid_geometry']:
|
||||
xmin, ymin, xmax, ymax = geo.bounds
|
||||
center = (((xmax - xmin) / 2) + xmin, ((ymax - ymin) / 2) + ymin)
|
||||
|
||||
|
@ -235,11 +258,22 @@ class ToolPDF(FlatCAMTool):
|
|||
for tool in exc_obj.tools:
|
||||
if exc_obj.tools[tool]['solid_geometry']:
|
||||
return
|
||||
app_obj.inform.emit(_("[ERROR_NOTCL] No geometry found in file: %s") % new_name)
|
||||
app_obj.inform.emit(_("[ERROR_NOTCL] No geometry found in file: %s") % outname)
|
||||
return "fail"
|
||||
else:
|
||||
# Gerber
|
||||
obj_type = 'gerber'
|
||||
|
||||
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
|
||||
|
||||
ret = self.app.new_object("excellon", outname, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] Open PDF file failed.'))
|
||||
return
|
||||
# Register recent file
|
||||
self.app.file_opened.emit("excellon", filename)
|
||||
# GUI feedback
|
||||
self.app.inform.emit(_("[success] Rendered: %s") % outname)
|
||||
|
||||
def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr):
|
||||
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
|
||||
|
||||
def obj_init(grb_obj, app_obj):
|
||||
|
||||
|
@ -263,16 +297,79 @@ class ToolPDF(FlatCAMTool):
|
|||
|
||||
grb_obj.solid_geometry = deepcopy(poly_buff)
|
||||
|
||||
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % (int(k) - 2)):
|
||||
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
|
||||
|
||||
ret = self.app.new_object(obj_type, new_name, obj_init, autoselected=False)
|
||||
ret = self.app.new_object('gerber', outname, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] Open PDF file failed.'))
|
||||
return
|
||||
# Register recent file
|
||||
self.app.file_opened.emit(obj_type, filename)
|
||||
self.app.file_opened.emit('gerber', filename)
|
||||
# GUI feedback
|
||||
self.app.inform.emit(_("[success] Opened: %s") % filename)
|
||||
self.app.inform.emit(_("[success] Rendered: %s") % outname)
|
||||
|
||||
def periodic_check(self, check_period):
|
||||
"""
|
||||
This function starts an QTimer and it will periodically check if parsing was done
|
||||
|
||||
:param check_period: time at which to check periodically if all plots finished to be plotted
|
||||
:return:
|
||||
"""
|
||||
|
||||
# self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
|
||||
# self.plot_thread.start()
|
||||
log.debug("ToolPDF --> Periodic Check started.")
|
||||
|
||||
try:
|
||||
self.check_thread.stop()
|
||||
except:
|
||||
pass
|
||||
|
||||
self.check_thread.setInterval(check_period)
|
||||
try:
|
||||
self.check_thread.timeout.disconnect(self.periodic_check_handler)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.check_thread.timeout.connect(self.periodic_check_handler)
|
||||
self.check_thread.start(QtCore.QThread.HighPriority)
|
||||
|
||||
def periodic_check_handler(self):
|
||||
"""
|
||||
If the parsing worker finished then start multithreaded rendering
|
||||
:return:
|
||||
"""
|
||||
# log.debug("checking parsing --> %s" % str(self.parsing_promises))
|
||||
|
||||
try:
|
||||
if not self.parsing_promises:
|
||||
self.check_thread.stop()
|
||||
# parsing finished start the layer rendering
|
||||
if self.pdf_parsed:
|
||||
obj_to_delete = []
|
||||
for object_name in self.pdf_parsed:
|
||||
filename = deepcopy(self.pdf_parsed[object_name]['filename'])
|
||||
pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf'])
|
||||
obj_to_delete.append(object_name)
|
||||
for k in pdf_content:
|
||||
ap_dict = pdf_content[k]
|
||||
if ap_dict:
|
||||
layer_nr = k
|
||||
if k == 0:
|
||||
self.app.worker_task.emit({'fcn': self.layer_rendering_as_excellon,
|
||||
'params': [filename, ap_dict, layer_nr]})
|
||||
else:
|
||||
self.app.worker_task.emit({'fcn': self.layer_rendering_as_gerber,
|
||||
'params': [filename, ap_dict, layer_nr]})
|
||||
# delete the object already processed so it will not be processed again for other objects
|
||||
# that were opened at the same time; like in drag & drop on GUI
|
||||
for obj_name in obj_to_delete:
|
||||
if obj_name in self.pdf_parsed:
|
||||
self.pdf_parsed.pop(obj_name)
|
||||
|
||||
log.debug("ToolPDF --> Periodic check finished.")
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def parse_pdf(self, pdf_content):
|
||||
path = dict()
|
||||
|
@ -301,9 +398,10 @@ class ToolPDF(FlatCAMTool):
|
|||
|
||||
# store the objects to be transformed into Gerbers
|
||||
object_dict = {}
|
||||
|
||||
# will serve as key in the object_dict
|
||||
object_nr = 1
|
||||
layer_nr = 1
|
||||
# create first object
|
||||
object_dict[layer_nr] = {}
|
||||
|
||||
# store the apertures here
|
||||
apertures_dict = {}
|
||||
|
@ -320,15 +418,11 @@ class ToolPDF(FlatCAMTool):
|
|||
clear_apertures_dict['0']['type'] = 'C'
|
||||
clear_apertures_dict['0']['solid_geometry'] = []
|
||||
|
||||
# create first object
|
||||
object_dict[object_nr] = apertures_dict
|
||||
object_nr += 1
|
||||
|
||||
# on stroke color change we create a new apertures dictionary and store the old one in a storage from where
|
||||
# it will be transformed into Gerber object
|
||||
old_color = [None, None ,None]
|
||||
|
||||
# signal that we have clear geometry and the geometry will be added to a special object_nr = 0
|
||||
# signal that we have clear geometry and the geometry will be added to a special layer_nr = 0
|
||||
flag_clear_geo = False
|
||||
|
||||
line_nr = 0
|
||||
|
@ -350,11 +444,12 @@ class ToolPDF(FlatCAMTool):
|
|||
# same color, do nothing
|
||||
continue
|
||||
else:
|
||||
object_dict[object_nr] = deepcopy(apertures_dict)
|
||||
object_nr += 1
|
||||
if apertures_dict:
|
||||
object_dict[layer_nr] = deepcopy(apertures_dict)
|
||||
apertures_dict.clear()
|
||||
layer_nr += 1
|
||||
|
||||
object_dict[object_nr] = dict()
|
||||
apertures_dict = {}
|
||||
object_dict[layer_nr] = dict()
|
||||
old_color = copy(color)
|
||||
# we make sure that the following geometry is added to the right storage
|
||||
flag_clear_geo = False
|
||||
|
@ -536,7 +631,6 @@ class ToolPDF(FlatCAMTool):
|
|||
y * self.point_to_unit_factor * scale_geo[1])
|
||||
|
||||
subpath['bezier'].append([start, c1, stop, stop])
|
||||
print(subpath['bezier'])
|
||||
current_point = stop
|
||||
continue
|
||||
|
||||
|
@ -747,18 +841,18 @@ class ToolPDF(FlatCAMTool):
|
|||
if path['rectangle']:
|
||||
for subp in path['rectangle']:
|
||||
geo = copy(subp)
|
||||
# close the subpath if it was not closed already
|
||||
if close_subpath is False and start_point is not None:
|
||||
geo.append(start_point)
|
||||
# # close the subpath if it was not closed already
|
||||
# if close_subpath is False and start_point is not None:
|
||||
# geo.append(start_point)
|
||||
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
||||
path_geo.append(geo_el)
|
||||
# the path was painted therefore initialize it
|
||||
path['rectangle'] = []
|
||||
else:
|
||||
geo = copy(subpath['rectangle'])
|
||||
# close the subpath if it was not closed already
|
||||
if close_subpath is False and start_point is not None:
|
||||
geo.append(start_point)
|
||||
# # close the subpath if it was not closed already
|
||||
# if close_subpath is False and start_point is not None:
|
||||
# geo.append(start_point)
|
||||
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
||||
path_geo.append(geo_el)
|
||||
subpath['rectangle'] = []
|
||||
|
@ -869,9 +963,9 @@ class ToolPDF(FlatCAMTool):
|
|||
# fill
|
||||
for subp in path['rectangle']:
|
||||
geo = copy(subp)
|
||||
# close the subpath if it was not closed already
|
||||
if close_subpath is False:
|
||||
geo.append(geo[0])
|
||||
# # close the subpath if it was not closed already
|
||||
# if close_subpath is False:
|
||||
# geo.append(geo[0])
|
||||
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
||||
fill_geo.append(geo_el)
|
||||
# stroke
|
||||
|
@ -884,9 +978,9 @@ class ToolPDF(FlatCAMTool):
|
|||
else:
|
||||
# fill
|
||||
geo = copy(subpath['rectangle'])
|
||||
# close the subpath if it was not closed already
|
||||
if close_subpath is False:
|
||||
geo.append(start_point)
|
||||
# # close the subpath if it was not closed already
|
||||
# if close_subpath is False:
|
||||
# geo.append(start_point)
|
||||
geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles)
|
||||
fill_geo.append(geo_el)
|
||||
# stroke
|
||||
|
@ -940,11 +1034,20 @@ class ToolPDF(FlatCAMTool):
|
|||
|
||||
# tidy up. copy the current aperture dict to the object dict but only if it is not empty
|
||||
if apertures_dict:
|
||||
object_dict[object_nr] = deepcopy(apertures_dict)
|
||||
object_dict[layer_nr] = deepcopy(apertures_dict)
|
||||
|
||||
if clear_apertures_dict['0']['solid_geometry']:
|
||||
object_dict[0] = deepcopy(clear_apertures_dict)
|
||||
|
||||
# delete keys (layers) with empty values
|
||||
empty_layers = []
|
||||
for layer in object_dict:
|
||||
if not object_dict[layer]:
|
||||
empty_layers.append(layer)
|
||||
for x in empty_layers:
|
||||
if x in object_dict:
|
||||
object_dict.pop(x)
|
||||
|
||||
return object_dict
|
||||
|
||||
def bezier_to_points(self, start, c1, c2, stop):
|
||||
|
@ -958,7 +1061,7 @@ class ToolPDF(FlatCAMTool):
|
|||
# with the final point P3. Intermediate values of t generate intermediate points along the curve.
|
||||
# The curve does not, in general, pass through the two control points P1 and P2
|
||||
|
||||
:return: LineString geometry
|
||||
:return: A list of point coordinates tuples (x, y)
|
||||
"""
|
||||
|
||||
# here we store the geometric points
|
||||
|
|
|
@ -0,0 +1,553 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://flatcam.org #
|
||||
# File Author: Marius Adrian Stanciu (c) #
|
||||
# Date: 4/24/2019 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
|
||||
from FlatCAMTool import FlatCAMTool
|
||||
# from copy import copy, deepcopy
|
||||
from ObjectCollection import *
|
||||
import time
|
||||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
import builtins
|
||||
|
||||
fcTranslate.apply_language('strings')
|
||||
if '_' not in builtins.__dict__:
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class ToolSub(FlatCAMTool):
|
||||
|
||||
toolName = _("Substract Tool")
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
||||
self.tools_frame = QtWidgets.QFrame()
|
||||
self.tools_frame.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout.addWidget(self.tools_frame)
|
||||
self.tools_box = QtWidgets.QVBoxLayout()
|
||||
self.tools_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.tools_frame.setLayout(self.tools_box)
|
||||
|
||||
# Title
|
||||
title_label = QtWidgets.QLabel("%s" % self.toolName)
|
||||
title_label.setStyleSheet("""
|
||||
QLabel
|
||||
{
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.tools_box.addWidget(title_label)
|
||||
|
||||
# Form Layout
|
||||
form_layout = QtWidgets.QFormLayout()
|
||||
self.tools_box.addLayout(form_layout)
|
||||
|
||||
self.gerber_title = QtWidgets.QLabel(_("<b>Gerber Objects</b>"))
|
||||
form_layout.addRow(self.gerber_title)
|
||||
|
||||
# Target Gerber Object
|
||||
self.target_gerber_combo = QtWidgets.QComboBox()
|
||||
self.target_gerber_combo.setModel(self.app.collection)
|
||||
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.setToolTip(
|
||||
_("Gerber object from which to substract\n"
|
||||
"the substractor Gerber object.")
|
||||
)
|
||||
|
||||
form_layout.addRow(self.target_gerber_label, self.target_gerber_combo)
|
||||
|
||||
# Substractor Gerber Object
|
||||
self.sub_gerber_combo = QtWidgets.QComboBox()
|
||||
self.sub_gerber_combo.setModel(self.app.collection)
|
||||
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.setToolTip(
|
||||
_("Gerber object that will be substracted\n"
|
||||
"from the target Gerber object.")
|
||||
)
|
||||
e_lab_1 = QtWidgets.QLabel('')
|
||||
|
||||
form_layout.addRow(self.sub_gerber_label, self.sub_gerber_combo)
|
||||
|
||||
self.intersect_btn = FCButton(_('Substract Gerber'))
|
||||
self.intersect_btn.setToolTip(
|
||||
_("Will remove the area occupied by the substractor\n"
|
||||
"Gerber from the Target Gerber.\n"
|
||||
"Can be used to remove the overlapping silkscreen\n"
|
||||
"over the soldermask.")
|
||||
)
|
||||
self.tools_box.addWidget(self.intersect_btn)
|
||||
self.tools_box.addWidget(e_lab_1)
|
||||
|
||||
# Form Layout
|
||||
form_geo_layout = QtWidgets.QFormLayout()
|
||||
self.tools_box.addLayout(form_geo_layout)
|
||||
|
||||
self.geo_title = QtWidgets.QLabel(_("<b>Geometry Objects</b>"))
|
||||
form_geo_layout.addRow(self.geo_title)
|
||||
|
||||
# Target Geometry Object
|
||||
self.target_geo_combo = QtWidgets.QComboBox()
|
||||
self.target_geo_combo.setModel(self.app.collection)
|
||||
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.setToolTip(
|
||||
_("Geometry object from which to substract\n"
|
||||
"the substractor Geometry object.")
|
||||
)
|
||||
|
||||
form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo)
|
||||
|
||||
# Substractor Geometry Object
|
||||
self.sub_geo_combo = QtWidgets.QComboBox()
|
||||
self.sub_geo_combo.setModel(self.app.collection)
|
||||
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.setToolTip(
|
||||
_("Geometry object that will be substracted\n"
|
||||
"from the target Geometry object.")
|
||||
)
|
||||
e_lab_1 = QtWidgets.QLabel('')
|
||||
|
||||
form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo)
|
||||
|
||||
self.intersect_geo_btn = FCButton(_('Substract Geometry'))
|
||||
self.intersect_geo_btn.setToolTip(
|
||||
_("Will remove the area occupied by the substractor\n"
|
||||
"Geometry from the Target Geometry.")
|
||||
)
|
||||
self.tools_box.addWidget(self.intersect_geo_btn)
|
||||
self.tools_box.addWidget(e_lab_1)
|
||||
|
||||
self.tools_box.addStretch()
|
||||
|
||||
# QTimer for periodic check
|
||||
self.check_thread = QtCore.QTimer()
|
||||
# Every time an intersection job is started we add a promise; every time an intersection job is finished
|
||||
# we remove a promise.
|
||||
# When empty we start the layer rendering
|
||||
self.promises = []
|
||||
|
||||
self.new_apertures = {}
|
||||
self.new_tools = {}
|
||||
self.new_solid_geometry = []
|
||||
|
||||
self.sub_union = None
|
||||
|
||||
self.sub_grb_obj = None
|
||||
self.sub_grb_obj_name = None
|
||||
self.target_grb_obj = None
|
||||
self.target_grb_obj_name = None
|
||||
|
||||
self.sub_geo_obj = None
|
||||
self.sub_geo_obj_name = None
|
||||
self.target_geo_obj = None
|
||||
self.target_geo_obj_name = None
|
||||
|
||||
# signal which type of substraction to do: "geo" or "gerber"
|
||||
self.sub_type = None
|
||||
|
||||
# store here the options from target_obj
|
||||
self.target_options = {}
|
||||
|
||||
try:
|
||||
self.intersect_btn.clicked.disconnect(self.on_grb_intersection_click)
|
||||
except:
|
||||
pass
|
||||
self.intersect_btn.clicked.connect(self.on_grb_intersection_click)
|
||||
|
||||
try:
|
||||
self.intersect_geo_btn.clicked.disconnect()
|
||||
except:
|
||||
pass
|
||||
self.intersect_geo_btn.clicked.connect(self.on_geo_intersection_click)
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='ALT+W', **kwargs)
|
||||
|
||||
def run(self, toggle=True):
|
||||
self.app.report_usage("ToolSub()")
|
||||
|
||||
if toggle:
|
||||
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
|
||||
if self.app.ui.splitter.sizes()[0] == 0:
|
||||
self.app.ui.splitter.setSizes([1, 1])
|
||||
else:
|
||||
try:
|
||||
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
|
||||
self.app.ui.splitter.setSizes([0, 1])
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if self.app.ui.splitter.sizes()[0] == 0:
|
||||
self.app.ui.splitter.setSizes([1, 1])
|
||||
|
||||
FlatCAMTool.run(self)
|
||||
self.set_tool_ui()
|
||||
|
||||
self.new_apertures.clear()
|
||||
self.new_tools.clear()
|
||||
self.new_solid_geometry = []
|
||||
self.target_options.clear()
|
||||
|
||||
self.app.ui.notebook.setTabText(2, _("Sub Tool"))
|
||||
|
||||
def set_tool_ui(self):
|
||||
self.tools_frame.show()
|
||||
|
||||
def on_grb_intersection_click(self):
|
||||
# reset previous values
|
||||
self.new_apertures.clear()
|
||||
self.new_solid_geometry = []
|
||||
self.sub_union = []
|
||||
|
||||
self.sub_type = "gerber"
|
||||
|
||||
self.target_grb_obj_name = self.target_gerber_combo.currentText()
|
||||
if self.target_grb_obj_name == '':
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded."))
|
||||
return
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
self.target_grb_obj = self.app.collection.get_by_name(self.target_grb_obj_name)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
|
||||
return "Could not retrieve object: %s" % self.target_grb_obj_name
|
||||
|
||||
self.sub_grb_obj_name = self.sub_gerber_combo.currentText()
|
||||
if self.sub_grb_obj_name == '':
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded."))
|
||||
return
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
self.sub_grb_obj = self.app.collection.get_by_name(self.sub_grb_obj_name)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
|
||||
return "Could not retrieve object: %s" % self.sub_grb_obj_name
|
||||
|
||||
# crate the new_apertures dict structure
|
||||
for apid in self.target_grb_obj.apertures:
|
||||
self.new_apertures[apid] = {}
|
||||
self.new_apertures[apid]['type'] = 'C'
|
||||
self.new_apertures[apid]['size'] = self.target_grb_obj.apertures[apid]['size']
|
||||
self.new_apertures[apid]['solid_geometry'] = []
|
||||
|
||||
geo_union_list = []
|
||||
for apid1 in self.sub_grb_obj.apertures:
|
||||
geo_union_list += self.sub_grb_obj.apertures[apid1]['solid_geometry']
|
||||
self.sub_union = cascaded_union(geo_union_list)
|
||||
|
||||
# add the promises
|
||||
for apid in self.target_grb_obj.apertures:
|
||||
self.promises.append(apid)
|
||||
|
||||
# start the QTimer to check for promises with 1 second period check
|
||||
self.periodic_check(500, reset=True)
|
||||
|
||||
for apid in self.target_grb_obj.apertures:
|
||||
geo = self.target_grb_obj.apertures[apid]['solid_geometry']
|
||||
self.app.worker_task.emit({'fcn': self.aperture_intersection,
|
||||
'params': [apid, geo]})
|
||||
|
||||
def aperture_intersection(self, apid, geo):
|
||||
new_solid_geometry = []
|
||||
log.debug("Working on promise: %s" % str(apid))
|
||||
|
||||
with self.app.proc_container.new(_("Parsing aperture %s geometry ..." % str(apid))):
|
||||
for geo_silk in geo:
|
||||
if geo_silk.intersects(self.sub_union):
|
||||
new_geo = geo_silk.difference(self.sub_union)
|
||||
new_geo = new_geo.buffer(0)
|
||||
if new_geo:
|
||||
if not new_geo.is_empty:
|
||||
new_solid_geometry.append(new_geo)
|
||||
else:
|
||||
new_solid_geometry.append(geo_silk)
|
||||
else:
|
||||
new_solid_geometry.append(geo_silk)
|
||||
else:
|
||||
new_solid_geometry.append(geo_silk)
|
||||
|
||||
if new_solid_geometry:
|
||||
while not self.new_apertures[apid]['solid_geometry']:
|
||||
self.new_apertures[apid]['solid_geometry'] = deepcopy(new_solid_geometry)
|
||||
time.sleep(0.5)
|
||||
|
||||
while True:
|
||||
# removal from list is done in a multithreaded way therefore not always the removal can be done
|
||||
# so we keep trying until it's done
|
||||
if apid not in self.promises:
|
||||
break
|
||||
|
||||
self.promises.remove(apid)
|
||||
time.sleep(0.5)
|
||||
|
||||
log.debug("Promise fulfilled: %s" % str(apid))
|
||||
|
||||
def new_gerber_object(self, outname):
|
||||
|
||||
def obj_init(grb_obj, app_obj):
|
||||
|
||||
grb_obj.apertures = deepcopy(self.new_apertures)
|
||||
|
||||
poly_buff = []
|
||||
for ap in self.new_apertures:
|
||||
for poly in self.new_apertures[ap]['solid_geometry']:
|
||||
poly_buff.append(poly)
|
||||
|
||||
work_poly_buff = cascaded_union(poly_buff)
|
||||
try:
|
||||
poly_buff = work_poly_buff.buffer(0.0000001)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
poly_buff = poly_buff.buffer(-0.0000001)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
grb_obj.solid_geometry = deepcopy(poly_buff)
|
||||
|
||||
with self.app.proc_container.new(_("Generating new object ...")):
|
||||
ret = self.app.new_object('gerber', outname, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
|
||||
return
|
||||
# Register recent file
|
||||
self.app.file_opened.emit('gerber', outname)
|
||||
# GUI feedback
|
||||
self.app.inform.emit(_("[success] Created: %s") % outname)
|
||||
|
||||
# cleanup
|
||||
self.new_apertures.clear()
|
||||
self.new_solid_geometry[:] = []
|
||||
self.sub_union[:] = []
|
||||
|
||||
def on_geo_intersection_click(self):
|
||||
# reset previous values
|
||||
self.new_tools.clear()
|
||||
self.target_options.clear()
|
||||
self.new_solid_geometry = []
|
||||
self.sub_union = []
|
||||
|
||||
self.sub_type = "geo"
|
||||
|
||||
self.target_geo_obj_name = self.target_geo_combo.currentText()
|
||||
if self.target_geo_obj_name == '':
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded."))
|
||||
return
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
self.target_geo_obj = self.app.collection.get_by_name(self.target_geo_obj_name)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.target_geo_obj_name)
|
||||
return "Could not retrieve object: %s" % self.target_grb_obj_name
|
||||
|
||||
self.sub_geo_obj_name = self.sub_geo_combo.currentText()
|
||||
if self.sub_geo_obj_name == '':
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded."))
|
||||
return
|
||||
|
||||
# Get source object.
|
||||
try:
|
||||
self.sub_geo_obj = self.app.collection.get_by_name(self.sub_geo_obj_name)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.sub_geo_obj_name)
|
||||
return "Could not retrieve object: %s" % self.sub_geo_obj_name
|
||||
|
||||
if self.sub_geo_obj.multigeo:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Currently, the Substractor geometry cannot be of type Multigeo."))
|
||||
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])
|
||||
|
||||
# crate the new_tools dict structure
|
||||
for tool in self.target_geo_obj.tools:
|
||||
self.new_tools[tool] = {}
|
||||
for key in self.target_geo_obj.tools[tool]:
|
||||
if key == 'solid_geometry':
|
||||
self.new_tools[tool][key] = []
|
||||
else:
|
||||
self.new_tools[tool][key] = deepcopy(self.target_geo_obj.tools[tool][key])
|
||||
|
||||
# add the promises
|
||||
if self.target_geo_obj.multigeo:
|
||||
for tool in self.target_geo_obj.tools:
|
||||
self.promises.append(tool)
|
||||
else:
|
||||
self.promises.append("single")
|
||||
|
||||
self.sub_union = cascaded_union(self.sub_geo_obj.solid_geometry)
|
||||
|
||||
# start the QTimer to check for promises with 0.5 second period check
|
||||
self.periodic_check(500, reset=True)
|
||||
|
||||
if self.target_geo_obj.multigeo:
|
||||
for tool in self.target_geo_obj.tools:
|
||||
geo = self.target_geo_obj.tools[tool]['solid_geometry']
|
||||
self.app.worker_task.emit({'fcn': self.toolgeo_intersection,
|
||||
'params': [tool, geo]})
|
||||
else:
|
||||
geo = self.target_geo_obj.solid_geometry
|
||||
self.app.worker_task.emit({'fcn': self.toolgeo_intersection,
|
||||
'params': ["single", geo]})
|
||||
|
||||
def toolgeo_intersection(self, tool, geo):
|
||||
new_geometry = []
|
||||
log.debug("Working on promise: %s" % str(tool))
|
||||
|
||||
if tool == "single":
|
||||
text = _("Parsing solid_geometry ...")
|
||||
else:
|
||||
text = _("Parsing tool %s geometry ...") % str(tool)
|
||||
|
||||
with self.app.proc_container.new(text):
|
||||
new_geo = (cascaded_union(geo)).difference(self.sub_union)
|
||||
if new_geo:
|
||||
if not new_geo.is_empty:
|
||||
new_geometry.append(new_geo)
|
||||
|
||||
if new_geometry:
|
||||
if tool == "single":
|
||||
while not self.new_solid_geometry:
|
||||
self.new_solid_geometry = deepcopy(new_geometry)
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
while not self.new_tools[tool]['solid_geometry']:
|
||||
self.new_tools[tool]['solid_geometry'] = deepcopy(new_geometry)
|
||||
time.sleep(0.5)
|
||||
|
||||
while True:
|
||||
# removal from list is done in a multithreaded way therefore not always the removal can be done
|
||||
# so we keep trying until it's done
|
||||
if tool not in self.promises:
|
||||
break
|
||||
|
||||
self.promises.remove(tool)
|
||||
time.sleep(0.5)
|
||||
log.debug("Promise fulfilled: %s" % str(tool))
|
||||
|
||||
def new_geo_object(self, outname):
|
||||
def obj_init(geo_obj, app_obj):
|
||||
|
||||
geo_obj.options = deepcopy(self.target_options)
|
||||
geo_obj.options['name'] = outname
|
||||
|
||||
if self.target_geo_obj.multigeo:
|
||||
geo_obj.tools = deepcopy(self.new_tools)
|
||||
# this turn on the FlatCAMCNCJob plot for multiple tools
|
||||
geo_obj.multigeo = True
|
||||
geo_obj.multitool = True
|
||||
else:
|
||||
geo_obj.solid_geometry = deepcopy(self.new_solid_geometry)
|
||||
try:
|
||||
geo_obj.tools = deepcopy(self.new_tools)
|
||||
for tool in geo_obj.tools:
|
||||
geo_obj.tools[tool]['solid_geometry'] = deepcopy(self.new_solid_geometry)
|
||||
except:
|
||||
pass
|
||||
|
||||
with self.app.proc_container.new(_("Generating new object ...")):
|
||||
ret = self.app.new_object('geometry', outname, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
|
||||
return
|
||||
# Register recent file
|
||||
self.app.file_opened.emit('geometry', outname)
|
||||
# GUI feedback
|
||||
self.app.inform.emit(_("[success] Created: %s") % outname)
|
||||
|
||||
# cleanup
|
||||
self.new_tools.clear()
|
||||
self.new_solid_geometry[:] = []
|
||||
self.sub_union[:] = []
|
||||
|
||||
def periodic_check(self, check_period, reset=False):
|
||||
"""
|
||||
This function starts an QTimer and it will periodically check if intersections are done
|
||||
|
||||
:param check_period: time at which to check periodically
|
||||
:param reset: will reset the timer
|
||||
:return:
|
||||
"""
|
||||
|
||||
log.debug("ToolSub --> Periodic Check started.")
|
||||
|
||||
try:
|
||||
self.check_thread.stop()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if reset:
|
||||
self.check_thread.setInterval(check_period)
|
||||
try:
|
||||
self.check_thread.timeout.disconnect(self.periodic_check_handler)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
self.check_thread.timeout.connect(self.periodic_check_handler)
|
||||
self.check_thread.start(QtCore.QThread.HighPriority)
|
||||
|
||||
def periodic_check_handler(self):
|
||||
"""
|
||||
If the intersections workers finished then start creating the solid_geometry
|
||||
:return:
|
||||
"""
|
||||
# log.debug("checking parsing --> %s" % str(self.parsing_promises))
|
||||
|
||||
|
||||
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]})
|
||||
|
||||
# reset the type of substraction for next time
|
||||
self.sub_type = None
|
||||
|
||||
log.debug("ToolSub --> Periodic check finished.")
|
||||
except Exception as e:
|
||||
log.debug("ToolSub().periodic_check_handler() --> %s" % str(e))
|
||||
traceback.print_exc()
|
||||
|
||||
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()))
|
||||
|
||||
self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
|
||||
self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
|
|
@ -16,5 +16,7 @@ from flatcamTools.ToolTransform import ToolTransform
|
|||
from flatcamTools.ToolSolderPaste import SolderPaste
|
||||
from flatcamTools.ToolPcbWizard import PcbWizard
|
||||
from flatcamTools.ToolPDF import ToolPDF
|
||||
from flatcamTools.ToolSub import ToolSub
|
||||
|
||||
|
||||
from flatcamTools.ToolShell import FCShell
|
||||
|
|
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
|
@ -20,7 +20,6 @@ pip3 install --upgrade Shapely
|
|||
pip3 install --upgrade vispy
|
||||
pip3 install --upgrade rtree
|
||||
pip3 install --upgrade pyopengl
|
||||
pip3 install --upgrade pyopengl-accelerate
|
||||
pip3 install --upgrade setuptools
|
||||
pip3 install --upgrade svg.path
|
||||
pip3 install --upgrade ortools
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 577 B |
Loading…
Reference in New Issue