diff --git a/FlatCAMApp.py b/FlatCAMApp.py index fe261376..887901f2 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -124,8 +124,8 @@ class App(QtCore.QObject): # ########################################################################## # ################## Version and VERSION DATE ############################## # ########################################################################## - version = 8.97 - version_date = "2019/09/27" + version = 8.98 + version_date = "2019/10/7" beta = True engine = '3D' @@ -492,6 +492,8 @@ class App(QtCore.QObject): "global_project_autohide": self.ui.general_defaults_form.general_gui_set_group.project_autohide_cb, "global_toggle_tooltips": self.ui.general_defaults_form.general_gui_set_group.toggle_tooltips_cb, "global_delete_confirmation": self.ui.general_defaults_form.general_gui_set_group.delete_conf_cb, + "global_cursor_type": self.ui.general_defaults_form.general_gui_set_group.cursor_radio, + "global_cursor_size": self.ui.general_defaults_form.general_gui_set_group.cursor_size_entry, # Gerber General "gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb, @@ -911,6 +913,8 @@ class App(QtCore.QObject): "global_hover": False, "global_selection_shape": True, "global_layout": "compact", + "global_cursor_type": "small", + "global_cursor_size": 20, # Gerber General "gerber_plot": True, @@ -1957,6 +1961,12 @@ class App(QtCore.QObject): self.ui.general_defaults_form.general_gui_set_group.layout_combo.activated.connect(self.on_layout) + # ################################################# + # ############ GUI SETTINGS SIGNALS ############### + # ################################################# + + self.ui.general_defaults_form.general_gui_set_group.cursor_radio.activated_custom.connect(self.on_cursor_type) + # ########## CNC Job related signals ############# self.ui.cncjob_defaults_form.cncjob_adv_opt_group.tc_variable_combo.currentIndexChanged[str].connect( self.on_cnc_custom_parameters) @@ -6265,6 +6275,13 @@ class App(QtCore.QObject): self.on_workspace() def on_layout(self, index=None, lay=None): + """ + Set the toolbars layout (location) + + :param index: + :param lay: type of layout to be set on the toolbard + :return: None + """ self.report_usage("on_layout()") if lay: current_layout = lay @@ -6397,6 +6414,20 @@ class App(QtCore.QObject): self.ui.snap_max_dist_entry.setText(str(self.defaults["global_snap_max"])) self.ui.grid_gap_link_cb.setChecked(True) + def on_cursor_type(self, val): + """ + + :param val: type of mouse cursor, set in Preferences ('small' or 'big') + :return: None + """ + + if val == 'small': + self.ui.general_defaults_form.general_gui_set_group.cursor_size_entry.setDisabled(False) + self.ui.general_defaults_form.general_gui_set_group.cursor_size_lbl.setDisabled(False) + else: + self.ui.general_defaults_form.general_gui_set_group.cursor_size_entry.setDisabled(True) + self.ui.general_defaults_form.general_gui_set_group.cursor_size_lbl.setDisabled(True) + def on_cnc_custom_parameters(self, signal_text): if signal_text == 'Parameters': return @@ -7826,7 +7857,7 @@ class App(QtCore.QObject): # Update cursor self.app_cursor.set_data(np.asarray([(pos[0], pos[1])]), - symbol='++', edge_color='black', size=20) + symbol='++', edge_color='black', size=self.defaults["global_cursor_size"]) else: pos = (pos_canvas[0], pos_canvas[1]) @@ -11172,7 +11203,10 @@ class App(QtCore.QObject): # Keys over plot enabled self.kp = self.plotcanvas.graph_event_connect('key_press', self.ui.keyPressEvent) - self.app_cursor = self.plotcanvas.new_cursor() + if self.defaults['global_cursor_type'] == 'small': + self.app_cursor = self.plotcanvas.new_cursor() + else: + self.app_cursor = self.plotcanvas.new_cursor(big=True) if self.ui.grid_snap_btn.isChecked(): self.app_cursor.enabled = True diff --git a/README.md b/README.md index 3d1b575c..e2d7194b 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ CAD program, and create G-Code for Isolation routing. 27.09.2019 -- optimized the toggle grid command - +- optimized the toggle axis command +- added posibility of using a big mouse cursor or a small mouse cursor. The big mouse cursor is made from 2 infinite lines. This was implemented for both graphic engines +- added ability to change the cursor size when the small mouse cursor is selected in Preferences -> General 27.09.2019 diff --git a/flatcamEditors/FlatCAMExcEditor.py b/flatcamEditors/FlatCAMExcEditor.py index 65f66ebd..4dc33d23 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/flatcamEditors/FlatCAMExcEditor.py @@ -3657,7 +3657,8 @@ class FlatCAMExcEditor(QtCore.QObject): x, y = self.app.geo_editor.snap(x, y) # Update cursor - self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20) + self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', + size=self.app.defaults["global_cursor_size"]) self.snap_x = x self.snap_y = y @@ -3705,7 +3706,8 @@ class FlatCAMExcEditor(QtCore.QObject): self.app.selection_type = None # Update cursor - self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20) + self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', + size=self.app.defaults["global_cursor_size"]) def on_canvas_key_release(self, event): self.key = None diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 276230aa..4a235daa 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -3755,7 +3755,8 @@ class FlatCAMGeoEditor(QtCore.QObject): x, y = self.snap(x, y) # Update cursor - self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20) + self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', + size=self.app.defaults["global_cursor_size"]) self.snap_x = x self.snap_y = y diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index 0302b410..ee69683f 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -4398,7 +4398,8 @@ class FlatCAMGrbEditor(QtCore.QObject): x, y = self.app.geo_editor.snap(x, y) # Update cursor - self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20) + self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', + size=self.app.defaults["global_cursor_size"]) self.snap_x = x self.snap_y = y diff --git a/flatcamGUI/PlotCanvas.py b/flatcamGUI/PlotCanvas.py index 869ed1a5..42d5dbcc 100644 --- a/flatcamGUI/PlotCanvas.py +++ b/flatcamGUI/PlotCanvas.py @@ -67,6 +67,14 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.draw_workspace() + self.line_parent = None + self.line_color = (0.3, 0.0, 0.0, 1.0) + self.cursor_v_line = InfiniteLine(pos=None, color=self.line_color, vertical=True, + parent=self.line_parent) + + self.cursor_h_line = InfiniteLine(pos=None, color=self.line_color, vertical=False, + parent=self.line_parent) + # if self.app.defaults['global_workspace'] is True: # if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() == 'MM': # self.wkspace_t = Line(pos=) @@ -80,6 +88,8 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): # TODO: Should be setting to show/hide CNC job annotations (global or per object) self.text_collection.enabled = True + self.c = None + # Keep VisPy canvas happy by letting it be "frozen" again. self.freeze() @@ -185,10 +195,30 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): # return sc return ShapeCollection(parent=self.view.scene, pool=self.fcapp.pool, **kwargs) - def new_cursor(self): - c = Cursor(pos=np.empty((0, 2)), parent=self.view.scene) - c.antialias = 0 - return c + def new_cursor(self, big=None): + if big is True: + self.c = CursorBig() + self.c.mouse_state_updated.connect(self.on_mouse_state) + self.c.mouse_position_updated.connect(self.on_mouse_position) + else: + self.c = Cursor(pos=np.empty((0, 2)), parent=self.view.scene) + self.c.antialias = 0 + return self.c + + def on_mouse_state(self, state): + if state: + self.cursor_h_line.parent = self.view.scene + self.cursor_v_line.parent = self.view.scene + else: + self.cursor_h_line.parent = None + self.cursor_v_line.parent = None + + def on_mouse_position(self, pos): + # self.line_color = color + + self.cursor_h_line.set_data(pos=pos[1], color=self.line_color) + self.cursor_v_line.set_data(pos=pos[0], color=self.line_color) + self.view.scene.update() def new_text_group(self, collection=None): if collection: @@ -247,3 +277,38 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): def on_pool_recreated(self, pool): self.shape_collection.pool = pool + + +class CursorBig(QtCore.QObject): + """ + This is a fake cursor to ensure compatibility with the OpenGL engine (VisPy). + This way I don't have to chane (disable) things related to the cursor all over when + using the low performance Matplotlib 2D graphic engine. + """ + + mouse_state_updated = QtCore.pyqtSignal(bool) + mouse_position_updated = QtCore.pyqtSignal(list) + + def __init__(self): + super().__init__() + + self._enabled = None + + @property + def enabled(self): + return True if self._enabled else False + + @enabled.setter + def enabled(self, value): + self._enabled = value + self.mouse_state_updated.emit(value) + + def set_data(self, pos, **kwargs): + """Internal event handler to draw the cursor when the mouse moves.""" + if 'edge_color' in kwargs: + color = kwargs['edge_color'] + else: + color = (0.0, 0.0, 0.0, 1.0) + + position = [pos[0][0], pos[0][1]] + self.mouse_position_updated.emit(position) diff --git a/flatcamGUI/PlotCanvasLegacy.py b/flatcamGUI/PlotCanvasLegacy.py index 3e3d8cd3..96df7e6a 100644 --- a/flatcamGUI/PlotCanvasLegacy.py +++ b/flatcamGUI/PlotCanvasLegacy.py @@ -159,6 +159,9 @@ class PlotCanvasLegacy(QtCore.QObject): self.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2) self.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2) + self.ch_line = None + self.cv_line = None + # The canvas is the top level container (FigureCanvasQTAgg) self.canvas = FigureCanvas(self.figure) @@ -207,6 +210,7 @@ class PlotCanvasLegacy(QtCore.QObject): self.pan_axes = [] self.panning = False self.mouse = [0, 0] + self.big_cursor = False # signal is the mouse is dragging self.is_dragging = False @@ -254,17 +258,19 @@ class PlotCanvasLegacy(QtCore.QObject): pass # log.debug("Cache updated the screen!") - def new_cursor(self, axes=None): + def new_cursor(self, axes=None, big=None): # if axes is None: # c = MplCursor(axes=self.axes, color='black', linewidth=1) # else: # c = MplCursor(axes=axes, color='black', linewidth=1) + if big is True: + self.big_cursor = True + self.ch_line = self.axes.axhline(color=(0.0, 0.0, 0.0), linewidth=1) + self.cv_line = self.axes.axvline(color=(0.0, 0.0, 0.0), linewidth=1) c = FakeCursor() - try: - c.mouse_state_updated.connect(self.clear_cursor) - except Exception as e: - print(str(e)) + c.mouse_state_updated.connect(self.clear_cursor) + return c def draw_cursor(self, x_pos, y_pos): @@ -277,18 +283,26 @@ class PlotCanvasLegacy(QtCore.QObject): """ # there is no point in drawing mouse cursor when panning as it jumps in a confusing way if self.app.app_cursor.enabled is True and self.panning is False: - try: - x, y = self.app.geo_editor.snap(x_pos, y_pos) + if self.big_cursor is False: + try: + x, y = self.app.geo_editor.snap(x_pos, y_pos) - # Pointer (snapped) - elements = self.axes.plot(x, y, 'k+', ms=33, mew=1, animated=True) - for el in elements: - self.axes.draw_artist(el) - except Exception as e: - # this happen at app initialization since self.app.geo_editor does not exist yet - # I could reshuffle the object instantiating order but what's the point? I could crash something else - # and that's pythonic, too - pass + # Pointer (snapped) + # The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the + # one in the OpenGL(3D) graphic engine + pointer_size = int(float(self.app.defaults["global_cursor_size"] ) * 1.65) + elements = self.axes.plot(x, y, 'k+', ms=pointer_size, mew=1, animated=True) + for el in elements: + self.axes.draw_artist(el) + except Exception as e: + # this happen at app initialization since self.app.geo_editor does not exist yet + # I could reshuffle the object instantiating order but what's the point? I could crash something else + # and that's pythonic, too + pass + else: + self.ch_line.set_ydata(y_pos) + self.cv_line.set_xdata(x_pos) + self.canvas.draw_idle() self.canvas.blit(self.axes.bbox) @@ -742,7 +756,6 @@ class FakeCursor(QtCore.QObject): def set_data(self, pos, **kwargs): """Internal event handler to draw the cursor when the mouse moves.""" - pass class ShapeCollectionLegacy: diff --git a/flatcamGUI/PreferencesUI.py b/flatcamGUI/PreferencesUI.py index d2a4dce9..baa46091 100644 --- a/flatcamGUI/PreferencesUI.py +++ b/flatcamGUI/PreferencesUI.py @@ -754,6 +754,29 @@ class GeneralGUISetGroupUI(OptionsGroupUI): "when hovering with mouse over items throughout the App.") ) + # Mouse Cursor Shape + self.cursor_lbl = QtWidgets.QLabel('%s:' % _('Mouse Cursor')) + self.cursor_lbl.setToolTip( + _("Choose a mouse cursor shape.\n" + "- Small -> with a customizable size.\n" + "- Big -> Infinite lines") + ) + + self.cursor_radio = RadioSet([ + {"label": _("Small"), "value": "small"}, + {"label": _("Big"), "value": "big"} + ], orientation='horizontal', stretch=False) + + self.cursor_size_lbl = QtWidgets.QLabel('%s:' % _('Mouse Cursor Size')) + self.cursor_size_lbl.setToolTip( + _("Set the size of the mouse cursor, in pixels.") + ) + + self.cursor_size_entry = FCSpinner() + self.cursor_size_entry.set_range(10, 70) + self.cursor_size_entry.setWrapping(True) + + # Add (label - input field) pair to the QFormLayout self.form_box.addRow(self.spacelabel, self.spacelabel) @@ -775,6 +798,8 @@ class GeneralGUISetGroupUI(OptionsGroupUI): self.form_box.addRow(self.project_autohide_label, self.project_autohide_cb) self.form_box.addRow(QtWidgets.QLabel('')) self.form_box.addRow(self.toggle_tooltips_label, self.toggle_tooltips_cb) + self.form_box.addRow(self.cursor_lbl, self.cursor_radio) + self.form_box.addRow(self.cursor_size_lbl, self.cursor_size_entry) # Add the QFormLayout that holds the Application general defaults # to the main layout of this TAB diff --git a/flatcamTools/ToolMeasurement.py b/flatcamTools/ToolMeasurement.py index 7a38dff1..672dd09a 100644 --- a/flatcamTools/ToolMeasurement.py +++ b/flatcamTools/ToolMeasurement.py @@ -346,7 +346,8 @@ class Measurement(FlatCAMTool): # Update cursor self.app.app_cursor.set_data(np.asarray([(pos[0], pos[1])]), - symbol='++', edge_color='black', size=20) + symbol='++', edge_color='black', + size=self.app.defaults["global_cursor_size"]) else: pos = (pos_canvas[0], pos_canvas[1]) diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index be3e4283..2e1c2164 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -1282,7 +1282,7 @@ class NonCopperClear(FlatCAMTool, Gerber): curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]), - symbol='++', edge_color='black', size=20) + symbol='++', edge_color='black', size=self.app.defaults["global_cursor_size"]) # update the positions on status bar self.app.ui.position_label.setText("    X: %.4f   " diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index cc02d796..49e931f4 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -1185,7 +1185,7 @@ class ToolPaint(FlatCAMTool, Gerber): curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]), - symbol='++', edge_color='black', size=20) + symbol='++', edge_color='black', size=self.app.defaults["global_cursor_size"]) # update the positions on status bar self.app.ui.position_label.setText("    X: %.4f   "