- in 2Sided Plugin clicking LMB and also pressing CTRL+Shift will add the click coordinates to the Drll Alignment Coordinates

- added support for all Plugins to handle the LMB click release event without connect/reconnect of the mouse events
- code refactoring in app_Main file
This commit is contained in:
Marius Stanciu
2021-10-04 17:42:18 +03:00
parent f4636ec71b
commit 411f410025
3 changed files with 221 additions and 115 deletions

View File

@@ -12,6 +12,9 @@ CHANGELOG for FlatCAM beta
- fixed a typo in the Object UI - fixed a typo in the Object UI
- in 2Sided Plugin advanced mode fixed the bounds calculation: if no object is selected on canvas then the object selected in Source Object is used - in 2Sided Plugin advanced mode fixed the bounds calculation: if no object is selected on canvas then the object selected in Source Object is used
- in 2Sided Plugin added a new typ of alignment drills: manual. This mode will no longer add pairs of drill holes mirrored against reference but only add in place drill holes - in 2Sided Plugin added a new typ of alignment drills: manual. This mode will no longer add pairs of drill holes mirrored against reference but only add in place drill holes
- in 2Sided Plugin clicking LMB and also pressing CTRL+Shift will add the click coordinates to the Drll Alignment Coordinates
- added support for all Plugins to handle the LMB click release event without connect/reconnect of the mouse events
- code refactoring in app_Main file
3.10.2021 3.10.2021

View File

@@ -118,7 +118,6 @@ class DblSidedTool(AppTool):
self.ui.object_type_combo.currentIndexChanged.connect(self.on_object_type) self.ui.object_type_combo.currentIndexChanged.connect(self.on_object_type)
self.ui.add_point_button.clicked.connect(self.on_point_add) self.ui.add_point_button.clicked.connect(self.on_point_add)
self.ui.add_drill_point_button.clicked.connect(self.on_drill_add)
self.ui.delete_drill_point_button.clicked.connect(self.on_drill_delete_last) self.ui.delete_drill_point_button.clicked.connect(self.on_drill_delete_last)
self.ui.box_type_radio.activated_custom.connect(self.on_combo_box_type) self.ui.box_type_radio.activated_custom.connect(self.on_combo_box_type)
@@ -433,21 +432,35 @@ class DblSidedTool(AppTool):
self.app.delete_selection_shape() self.app.delete_selection_shape()
self.ui.axis_location.set_value('point') self.ui.axis_location.set_value('point')
# set the reference point for mirror # set the reference point for mirror
self.ui.point_entry.set_value(center_pt_coords) self.ui.point_entry.set_value(center_pt_coords)
self.on_exit() self.on_exit()
self.app.inform.emit('[success] %s' % _("Mirror reference point set.")) self.app.inform.emit('[success] %s' % _("Mirror reference point set."))
break
elif event.button == right_button and self.app.event_is_dragging is False: elif event.button == right_button and self.app.event_is_dragging is False:
self.on_exit(cancelled=True) self.on_exit(cancelled=True)
def on_mouse_plugin_click_release(self):
modifiers = QtWidgets.QApplication.keyboardModifiers()
# if modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier:
# clip_val = self.app.clipboard.text()
# self.ui.point_entry.set_value(clip_val)
if modifiers == QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier:
clip_val = self.app.clipboard.text().replace("[", '').replace("]", '')
self.ui.alignment_holes.set_value(clip_val)
else:
return
self.drill_values = clip_val
def on_exit(self, cancelled=None): def on_exit(self, cancelled=None):
self.app.call_source = "app" self.app.call_source = "app"
self.app.ui.notebook.setDisabled(False) self.app.ui.notebook.setDisabled(False)
self.disconnect_events()
if cancelled is True: if cancelled is True:
self.app.delete_selection_shape() self.app.delete_selection_shape()
self.disconnect_events()
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request.")) self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))
def disconnect_events(self): def disconnect_events(self):
@@ -506,11 +519,6 @@ class DblSidedTool(AppTool):
(self.decimals, self.app.pos[0], self.decimals, self.app.pos[1]) (self.decimals, self.app.pos[0], self.decimals, self.app.pos[1])
self.ui.point_entry.set_value(val) self.ui.point_entry.set_value(val)
def on_drill_add(self):
self.drill_values += (self.app.defaults["global_point_clipboard_format"] %
(self.decimals, self.app.pos[0], self.decimals, self.app.pos[1])) + ','
self.ui.alignment_holes.set_value(self.drill_values)
def on_drill_delete_last(self): def on_drill_delete_last(self):
drill_values_without_last_tupple = self.drill_values.rpartition('(')[0] drill_values_without_last_tupple = self.drill_values.rpartition('(')[0]
self.drill_values = drill_values_without_last_tupple self.drill_values = drill_values_without_last_tupple
@@ -897,13 +905,6 @@ class DsidedUI:
"The (x, y) coordinates are captured by pressing SHIFT key\n" "The (x, y) coordinates are captured by pressing SHIFT key\n"
"and left mouse button click on canvas or you can enter the coordinates manually.") "and left mouse button click on canvas or you can enter the coordinates manually.")
) )
# self.add_point_button.setStyleSheet("""
# QPushButton
# {
# font-weight: bold;
# }
# """)
# self.add_point_button.setMinimumWidth(60)
pr_hlay.addWidget(self.point_entry, stretch=1) pr_hlay.addWidget(self.point_entry, stretch=1)
pr_hlay.addWidget(self.add_point_button) pr_hlay.addWidget(self.add_point_button)
@@ -1072,41 +1073,21 @@ class DsidedUI:
"- one drill in mirror position over the axis selected above in the 'Align Axis'.") "- one drill in mirror position over the axis selected above in the 'Align Axis'.")
) )
grid4.addWidget(self.ah_label, 8, 0, 1, 2)
self.alignment_holes = NumericalEvalTupleEntry(border_color='#0069A9') self.alignment_holes = NumericalEvalTupleEntry(border_color='#0069A9')
self.alignment_holes.setPlaceholderText(_("Drill coordinates")) self.alignment_holes.setPlaceholderText(_("Drill coordinates"))
grid4.addWidget(self.ah_label, 8, 0, 1, 2) self.delete_drill_point_button = QtWidgets.QToolButton()
grid4.addWidget(self.alignment_holes, 9, 0, 1, 2)
self.add_drill_point_button = FCButton(_("Add"))
self.add_drill_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
self.add_drill_point_button.setToolTip(
_("Add alignment drill holes coordinates in the format: (x1, y1), (x2, y2), ... \n"
"on one side of the alignment axis.\n\n"
"The coordinates set can be obtained:\n"
"- press SHIFT key and left mouse clicking on canvas. Then click Add.\n"
"- press SHIFT key and left mouse clicking on canvas. Then Ctrl+V in the field.\n"
"- press SHIFT key and left mouse clicking on canvas. Then RMB click in the field and click Paste.\n"
"- by entering the coords manually in the format: (x1, y1), (x2, y2), ...")
)
# self.add_drill_point_button.setStyleSheet("""
# QPushButton
# {
# font-weight: bold;
# }
# """)
self.delete_drill_point_button = FCButton(_("Delete Last"))
self.delete_drill_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/trash32.png')) self.delete_drill_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/trash32.png'))
self.delete_drill_point_button.setToolTip( self.delete_drill_point_button.setToolTip(
_("Delete the last coordinates tuple in the list.") _("Delete the last coordinates tuple in the list.")
) )
drill_hlay = QtWidgets.QHBoxLayout()
drill_hlay.addWidget(self.add_drill_point_button) hlay = QtWidgets.QHBoxLayout()
drill_hlay.addWidget(self.delete_drill_point_button) hlay.addWidget(self.alignment_holes)
hlay.addWidget(self.delete_drill_point_button)
grid4.addLayout(drill_hlay, 10, 0, 1, 2) grid4.addLayout(hlay, 9, 0, 1, 2)
FCGridLayout.set_common_column_size([obj_grid, grid_bounds, grid_mirror, grid_box_ref, grid4], 0) FCGridLayout.set_common_column_size([obj_grid, grid_bounds, grid_mirror, grid_box_ref, grid4], 0)

View File

@@ -1231,6 +1231,9 @@ class App(QtCore.QObject):
self.corners_tool = None self.corners_tool = None
self.etch_tool = None self.etch_tool = None
# when this list will get populated will contain a list of references to all the Plugins in this APp
self.app_plugins = []
# always install tools only after the shell is initialized because the self.inform.emit() depends on shell # always install tools only after the shell is initialized because the self.inform.emit() depends on shell
try: try:
self.install_tools() self.install_tools()
@@ -1988,6 +1991,46 @@ class App(QtCore.QObject):
self.pcb_wizard_tool.install(icon=QtGui.QIcon(self.resource_location + '/drill32.png'), self.pcb_wizard_tool.install(icon=QtGui.QIcon(self.resource_location + '/drill32.png'),
pos=self.ui.menufileimport) pos=self.ui.menufileimport)
# create a list of plugins references
self.app_plugins = [
self.dblsidedtool,
self.distance_tool,
self.distance_min_tool,
self.panelize_tool,
self.film_tool,
self.paste_tool,
self.calculator_tool,
self.rules_tool,
self.sub_tool,
self.move_tool,
self.cutout_tool,
self.ncclear_tool,
self.paint_tool,
self.isolation_tool,
self.follow_tool,
self.drilling_tool,
self.milling_tool,
self.levelling_tool,
self.optimal_tool,
self.transform_tool,
self.report_tool,
self.pdf_tool,
self.image_tool,
self.pcb_wizard_tool,
self.cal_exc_tool,
self.qrcode_tool,
self.copper_thieving_tool,
self.fiducial_tool,
self.extract_tool,
self.align_objects_tool,
self.punch_tool,
self.invert_tool,
self.corners_tool,
self.etch_tool
]
self.log.debug("Tools are installed.") self.log.debug("Tools are installed.")
def remove_tools(self): def remove_tools(self):
@@ -2012,10 +2055,17 @@ class App(QtCore.QObject):
self.log.debug("init_tools()") self.log.debug("init_tools()")
# delete the data currently in the Tools Tab and the Tab itself # delete the data currently in the Tools Tab and the Tab itself
widget = QtWidgets.QTabWidget.widget(self.ui.notebook, 2) found_idx = None
for tab_idx in range(self.ui.notebook.count()):
if self.ui.notebook.widget(tab_idx).objectName() == "plugin_tab":
found_idx = tab_idx
print(found_idx)
break
remove_idx = found_idx if found_idx else 2
widget = QtWidgets.QTabWidget.widget(self.ui.notebook, remove_idx)
if widget is not None: if widget is not None:
widget.deleteLater() widget.deleteLater()
self.ui.notebook.removeTab(2) self.ui.notebook.removeTab(remove_idx)
# rebuild the Tools Tab # rebuild the Tools Tab
# self.ui.plugin_tab = QtWidgets.QWidget() # self.ui.plugin_tab = QtWidgets.QWidget()
@@ -2677,7 +2727,7 @@ class App(QtCore.QObject):
break break
if found_idx: if found_idx:
self.ui.notebook.setCurrentWidget(self.ui.properties_tab) self.ui.notebook.setCurrentWidget(self.ui.properties_tab)
self.ui.notebook.removeTab(2) self.ui.notebook.removeTab(found_idx)
if edited_obj.kind == 'geometry': if edited_obj.kind == 'geometry':
obj_type = "Geometry" obj_type = "Geometry"
@@ -6701,7 +6751,7 @@ class App(QtCore.QObject):
break break
if found_idx: if found_idx:
self.ui.notebook.setCurrentWidget(self.ui.properties_tab) self.ui.notebook.setCurrentWidget(self.ui.properties_tab)
self.ui.notebook.removeTab(2) self.ui.notebook.removeTab(found_idx)
# HACK: the content was removed but let's create it again # HACK: the content was removed but let's create it again
self.ui.plugin_tab = QtWidgets.QWidget() self.ui.plugin_tab = QtWidgets.QWidget()
@@ -6759,7 +6809,7 @@ class App(QtCore.QObject):
self.app_obj.object_changed.emit(obj) self.app_obj.object_changed.emit(obj)
self.inform.emit('[success] %s.' % _("Flip on Y axis done")) self.inform.emit('[success] %s.' % _("Flip on Y axis done"))
except Exception as e: except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e))) self.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
return return
def on_flipx(self): def on_flipx(self):
@@ -6805,7 +6855,7 @@ class App(QtCore.QObject):
self.app_obj.object_changed.emit(obj) self.app_obj.object_changed.emit(obj)
self.inform.emit('[success] %s.' % _("Flip on X axis done")) self.inform.emit('[success] %s.' % _("Flip on X axis done"))
except Exception as e: except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e))) self.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
return return
def on_rotate(self, silent=False, preset=None): def on_rotate(self, silent=False, preset=None):
@@ -7310,90 +7360,143 @@ class App(QtCore.QObject):
# if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
# canvas menu # canvas menu
if event.button == right_button and self.ui.popMenu.mouse_is_panning is False: # right click if event.button == right_button and self.ui.popMenu.mouse_is_panning is False: # right click
self.ui.popMenu.mouse_is_panning = False self.on_mouse_context_menu()
if self.inhibit_context_menu is False:
self.cursor = QtGui.QCursor()
self.populate_cmenu_grids()
self.ui.popMenu.popup(self.cursor.pos())
# if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
# selection and then select a type of selection ("enclosing" or "touching") # selection and then select a type of selection ("enclosing" or "touching")
if event.button == 1: # left click if event.button == 1: # left click
modifiers = QtWidgets.QApplication.keyboardModifiers() key_modifier = QtWidgets.QApplication.keyboardModifiers()
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard shift_modifier_key = QtCore.Qt.KeyboardModifier.ShiftModifier
if modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier: ctrl_modifier_key = QtCore.Qt.KeyboardModifier.ControlModifier
# do not auto open the Project Tab ctrl_shift_modifier_key = ctrl_modifier_key | shift_modifier_key
self.click_noproject = True
self.clipboard.setText( if key_modifier == shift_modifier_key or key_modifier == ctrl_shift_modifier_key:
self.defaults["global_point_clipboard_format"] % self.on_mouse_and_key_modifiers(position=self.pos, modifiers=key_modifier)
(self.decimals, self.pos[0], self.decimals, self.pos[1]) self.on_mouse_plugin_click_release()
)
self.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
return return
else:
self.on_mouse_plugin_click_release()
# the object selection on canvas does not work for App Tools or for Editors # the object selection on canvas will not work for App Tools or for Editors
if self.call_source != 'app': if self.call_source != 'app':
return return
if self.doubleclick is True: if self.doubleclick is True:
self.doubleclick = False self.on_mouse_double_click()
if self.collection.get_selected(): return
self.ui.notebook.setCurrentWidget(self.ui.properties_tab)
if self.ui.splitter.sizes()[0] == 0: # WORKAROUND for LEGACY MODE
self.ui.splitter.setSizes([1, 1]) if self.is_legacy is True:
try: # if there is no move on canvas then we have no dragging selection
# delete the selection shape(S) as it may be in the way if self.dx == 0 or self.dy == 0:
self.delete_selection_shape() self.selection_type = None
self.delete_hover_shape()
except Exception as e: if self.selection_type is not None:
self.log.error( try:
"FlatCAMApp.on_mouse_click_release_over_plot() double click --> Error: %s" % str(e)) self.selection_area_handler(self.pos, pos, self.selection_type)
return self.selection_type = None
except Exception as e:
self.log.error(
"FlatCAMApp.on_mouse_click_release_over_plot() select area --> Error: %s" % str(e))
return
if key_modifier == shift_modifier_key:
mod_key = 'Shift'
elif key_modifier == ctrl_modifier_key:
mod_key = 'Control'
else: else:
# WORKAROUND for LEGACY MODE mod_key = None
if self.is_legacy is True:
# if there is no move on canvas then we have no dragging selection
if self.dx == 0 or self.dy == 0:
self.selection_type = None
if self.selection_type is not None: try:
try: if self.command_active is None:
self.selection_area_handler(self.pos, pos, self.selection_type) if mod_key == self.defaults["global_mselect_key"]:
self.selection_type = None # If the modifier key is pressed when the LMB is clicked then if the object is selected it will
except Exception as e: # deselect, and if it's not selected then it will be selected
self.log.error( self.select_objects(key='multisel')
"FlatCAMApp.on_mouse_click_release_over_plot() select area --> Error: %s" % str(e))
return
else:
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if key_modifier == QtCore.Qt.KeyboardModifier.ShiftModifier:
mod_key = 'Shift'
elif key_modifier == QtCore.Qt.KeyboardModifier.ControlModifier:
mod_key = 'Control'
else: else:
mod_key = None # If there is no active command (self.command_active is None) then we check if
# we clicked on a object by checking the bounding limits against mouse click position
self.select_objects()
try: self.delete_hover_shape()
if self.command_active is None: except Exception as e:
# If the CTRL key is pressed when the LMB is clicked then if the object is selected it will self.log.error(
# deselect, and if it's not selected then it will be selected "FlatCAMApp.on_mouse_click_release_over_plot() select click --> Error: %s" % str(e))
# If there is no active command (self.command_active is None) then we check if we clicked return
# on a object by checking the bounding limits against mouse click position
if mod_key == self.defaults["global_mselect_key"]:
self.select_objects(key='multisel')
else:
# If there is no active command (self.command_active is None) then we check if
# we clicked on a object by checking the bounding limits against mouse click position
self.select_objects()
self.delete_hover_shape() def on_mouse_double_click(self):
except Exception as e: """
self.log.error( Called when mouse double clicking on canvas.
"FlatCAMApp.on_mouse_click_release_over_plot() select click --> Error: %s" % str(e))
return :return:
"""
self.doubleclick = False
if self.collection.get_selected():
self.ui.notebook.setCurrentWidget(self.ui.properties_tab)
if self.ui.splitter.sizes()[0] == 0:
self.ui.splitter.setSizes([1, 1])
try:
# delete the selection shape(S) as it may be in the way
self.delete_selection_shape()
self.delete_hover_shape()
except Exception as e:
self.log.error(
"FlatCAMApp.on_mouse_click_release_over_plot() double click --> Error: %s" % str(e))
def on_mouse_and_key_modifiers(self, position, modifiers):
"""
Called when the mouse is left-clicked on canvas and simultaneously a key modifier
(Ctrl, AAlt, Shift) is pressed.
:param position: A tupple made of the clicked position x, y coordinates
:param modifiers: Key modifiers (Ctrl, Alt, Shift or a combination of them)
:return:
"""
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
if modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier:
# do not auto open the Project Tab
self.click_noproject = True
self.clipboard.setText(
self.defaults["global_point_clipboard_format"] %
(self.decimals,position[0], self.decimals, position[1])
)
self.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
elif modifiers == QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier:
try:
old_clipb = eval(self.clipboard.text())
except Exception as err:
# self.log.error("App.on_mouse_and_key_modifiers() --> %s" % str(err))
old_clipb = None
clip_pos_val = (
self.dec_format(position[0], self.decimals),
self.dec_format(position[1], self.decimals)
)
clip_text = "(%s, %s)" % (str(clip_pos_val[0]), str(clip_pos_val[1]))
if old_clipb is None or old_clipb == '':
self.clipboard.setText(clip_text)
else:
if isinstance(old_clipb, list):
old_clipb.append(clip_pos_val)
else:
old_clipb = [old_clipb, clip_pos_val]
self.clipboard.setText(str(old_clipb))
self.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
def on_mouse_context_menu(self):
"""
Display a context menu when mouse right-clicking on canvas.
:return:
"""
if self.inhibit_context_menu is False:
self.cursor = QtGui.QCursor()
self.populate_cmenu_grids()
self.ui.popMenu.popup(self.cursor.pos())
def selection_area_handler(self, start_pos, end_pos, sel_type): def selection_area_handler(self, start_pos, end_pos, sel_type):
""" """
@@ -7613,6 +7716,25 @@ class App(QtCore.QObject):
tx=_("selected")) tx=_("selected"))
) )
def on_mouse_plugin_click_release(self):
"""
Handle specific tasks in the Plugins for the mouse click release
:return:
"""
if self.ui.notebook.currentWidget().objectName() == "plugin_tab":
tab_idx = self.ui.notebook.currentIndex()
for plugin in self.app_plugins:
# execute this only for the current active plugin
if self.ui.notebook.tabText(tab_idx) == plugin.pluginName:
try:
plugin.on_mouse_plugin_click_release()
except AttributeError:
# not all plugins have this implemented
# print("This does not have it", self.ui.notebook.tabText(tab_idx))
pass
def delete_hover_shape(self): def delete_hover_shape(self):
self.hover_shapes.clear() self.hover_shapes.clear()
self.hover_shapes.redraw() self.hover_shapes.redraw()