Просмотр исходного кода

- legacy graphic engine - made the mouse events work (click, release, doubleclick, dragging)
- legacy graphic engine - made the key events work (simple or with modifiers)
- legacy graphic engine - made the mouse cursor work (enabled/disabled, position report); snapping is not moving the cursor yet

Marius Stanciu 6 лет назад
Родитель
Сommit
011e80c0ce

+ 89 - 26
FlatCAMApp.py

@@ -39,6 +39,8 @@ from array import array
 from ObjectCollection import *
 from FlatCAMObj import *
 from flatcamGUI.PlotCanvas import *
+from flatcamGUI.PlotCanvasLegacy import *
+
 from flatcamGUI.FlatCAMGUI import *
 from FlatCAMCommon import LoudDict
 from FlatCAMPostProc import load_postprocessors
@@ -440,6 +442,7 @@ class App(QtCore.QObject):
         self.defaults_form_fields = {
             # General App
             "units": self.ui.general_defaults_form.general_app_group.units_radio,
+            "global_graphic_engine": self.ui.general_defaults_form.general_app_group.ge_radio,
             "global_app_level": self.ui.general_defaults_form.general_app_group.app_level_radio,
             "global_portable": self.ui.general_defaults_form.general_app_group.portability_cb,
             "global_language": self.ui.general_defaults_form.general_app_group.language_cb,
@@ -825,6 +828,7 @@ class App(QtCore.QObject):
             "global_serial": 0,
             "global_stats": {},
             "global_tabs_detachable": True,
+            "global_graphic_engine": '3D',
             "global_app_level": 'b',
             "global_portable": False,
             "global_language": 'English',
@@ -1584,6 +1588,13 @@ class App(QtCore.QObject):
         # ###############################################
         # ############# SETUP Plot Area #################
         # ###############################################
+
+        # determine if the Legacy Graphic Engine is to be used or the OpenGL one
+        if self.defaults["global_graphic_engine"] == '3D':
+            self.is_legacy = False
+        else:
+            self.is_legacy = True
+
         if show_splash:
             self.splash.showMessage(_("FlatCAM is initializing ...\n"
                                       "Canvas initialization started."),
@@ -6694,12 +6705,12 @@ class App(QtCore.QObject):
                     for obj in self.collection.get_list():
                         obj.plot()
                     self.plotcanvas.fit_view()
-                self.plotcanvas.vis_disconnect('mouse_press', self.on_set_zero_click)
+                self.plotcanvas.graph_event_disconnect('mouse_press', self.on_set_zero_click)
 
             self.worker_task.emit({'fcn': worker_task, 'params': []})
 
         self.inform.emit(_('Click to set the origin ...'))
-        self.plotcanvas.vis_connect('mouse_press', self.on_set_zero_click)
+        self.plotcanvas.graph_event_connect('mouse_press', self.on_set_zero_click)
 
         # first disconnect it as it may have been used by something else
         try:
@@ -7525,19 +7536,31 @@ class App(QtCore.QObject):
         """
         self.pos = []
 
+        if self.is_legacy is False:
+            event_pos = event.pos
+            if self.defaults["global_pan_button"] == '2':
+                pan_button = 2
+            else:
+                pan_button = 3
+            # Set the mouse button for panning
+            self.plotcanvas.view.camera.pan_button_setting = pan_button
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
+            if self.defaults["global_pan_button"] == '2':
+                pan_button = 3
+            else:
+                pan_button = 2
+
         # So it can receive key presses
         self.plotcanvas.native.setFocus()
-        # Set the mouse button for panning
-        self.plotcanvas.view.camera.pan_button_setting = self.defaults['global_pan_button']
 
-        self.pos_canvas = self.plotcanvas.translate_coords(event.pos)
+        self.pos_canvas = self.plotcanvas.translate_coords(event_pos)
 
         if self.grid_status() == True:
             self.pos = self.geo_editor.snap(self.pos_canvas[0], self.pos_canvas[1])
-            self.app_cursor.enabled = True
         else:
             self.pos = (self.pos_canvas[0], self.pos_canvas[1])
-            self.app_cursor.enabled = False
 
         try:
             modifiers = QtWidgets.QApplication.keyboardModifiers()
@@ -7565,7 +7588,8 @@ class App(QtCore.QObject):
             App.log.debug("App.on_mouse_click_over_plot() --> Outside plot? --> %s" % str(e))
 
     def on_double_click_over_plot(self, event):
-        self.doubleclick = True
+        if event.button == 1:
+            self.doubleclick = True
 
     def on_mouse_move_over_plot(self, event, origin_click=None):
         """
@@ -7576,29 +7600,49 @@ class App(QtCore.QObject):
         :return: None
         """
 
+        if self.is_legacy is False:
+            event_pos = event.pos
+            if self.defaults["global_pan_button"] == '2':
+                pan_button = 2
+            else:
+                pan_button = 3
+            event_is_dragging = event.is_dragging
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
+            if self.defaults["global_pan_button"] == '2':
+                pan_button = 3
+            else:
+                pan_button = 2
+            event_is_dragging = self.plotcanvas.is_dragging
+
         # So it can receive key presses
         self.plotcanvas.native.setFocus()
-        self.pos_jump = event.pos
+        self.pos_jump = event_pos
 
         self.ui.popMenu.mouse_is_panning = False
 
         if not origin_click:
             # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
-            if event.button == 2 and event.is_dragging == 1:
+            if event.button == pan_button and event_is_dragging == 1:
                 self.ui.popMenu.mouse_is_panning = True
                 return
 
         if self.rel_point1 is not None:
             try:  # May fail in case mouse not within axes
-                pos_canvas = self.plotcanvas.translate_coords(event.pos)
+                pos_canvas = self.plotcanvas.translate_coords(event_pos)
+
                 if self.grid_status() == True:
                     pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1])
-                    self.app_cursor.enabled = True
-                    # Update cursor
-                    self.app_cursor.set_data(np.asarray([(pos[0], pos[1])]), symbol='++', edge_color='black', size=20)
+
+                    if self.is_legacy is False:
+                        # Update cursor
+                        self.app_cursor.set_data(np.asarray([(pos[0], pos[1])]),
+                                                 symbol='++', edge_color='black', size=20)
+                    else:
+                        self.app_cursor.set_data((pos[0], pos[1]))
                 else:
                     pos = (pos_canvas[0], pos_canvas[1])
-                    self.app_cursor.enabled = False
 
                 self.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
                                                "<b>Y</b>: %.4f" % (pos[0], pos[1]))
@@ -7610,7 +7654,7 @@ class App(QtCore.QObject):
                 self.mouse = [pos[0], pos[1]]
 
                 # if the mouse is moved and the LMB is clicked then the action is a selection
-                if event.is_dragging == 1 and event.button == 1:
+                if event_is_dragging == 1 and event.button == 1:
                     self.delete_selection_shape()
                     if dx < 0:
                         self.draw_moving_selection_shape(self.pos, pos, color=self.defaults['global_alt_sel_line'],
@@ -7664,7 +7708,16 @@ class App(QtCore.QObject):
         :return:
         """
         pos = 0, 0
-        pos_canvas = self.plotcanvas.translate_coords(event.pos)
+
+        if self.is_legacy is False:
+            event_pos = event.pos
+            right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
+            right_button = 3
+
+        pos_canvas = self.plotcanvas.translate_coords(event_pos)
         if self.grid_status() == True:
             pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1])
         else:
@@ -7672,7 +7725,7 @@ 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
         # canvas menu
-        if event.button == 2:  # right click
+        if event.button == right_button:  # right click
             if self.ui.popMenu.mouse_is_panning is False:
                 self.cursor = QtGui.QCursor()
                 self.populate_cmenu_grids()
@@ -10893,22 +10946,32 @@ class App(QtCore.QObject):
         else:
             plot_container = self.ui.right_layout
 
-        self.plotcanvas = PlotCanvas(plot_container, self)
+        if self.is_legacy is False:
+            self.plotcanvas = PlotCanvas(plot_container, self)
+        else:
+            self.plotcanvas = PlotCanvasLegacy(plot_container, self)
 
         # So it can receive key presses
         self.plotcanvas.native.setFocus()
 
-        self.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_over_plot)
-        self.plotcanvas.vis_connect('mouse_press', self.on_mouse_click_over_plot)
-        self.plotcanvas.vis_connect('mouse_release', self.on_mouse_click_release_over_plot)
-        self.plotcanvas.vis_connect('mouse_double_click', self.on_double_click_over_plot)
+        self.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move_over_plot)
+        self.plotcanvas.graph_event_connect('mouse_press', self.on_mouse_click_over_plot)
+        self.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release_over_plot)
+        self.plotcanvas.graph_event_connect('mouse_double_click', self.on_double_click_over_plot)
 
         # Keys over plot enabled
-        self.plotcanvas.vis_connect('key_press', self.ui.keyPressEvent)
+        self.plotcanvas.graph_event_connect('key_press', self.ui.keyPressEvent)
 
         self.app_cursor = self.plotcanvas.new_cursor()
-        self.app_cursor.enabled = False
-        self.hover_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1)
+        if self.ui.grid_snap_btn.isChecked():
+            self.app_cursor.enabled = True
+        else:
+            self.app_cursor.enabled = False
+
+        if self.is_legacy is False:
+            self.hover_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1)
+        else:
+            self.hover_shapes = ShapeCollectionLegacy()
 
     def on_zoom_fit(self, event):
         """

+ 19 - 1
FlatCAMObj.py

@@ -74,6 +74,9 @@ class FlatCAMObj(QtCore.QObject):
         # store here the default data for Geometry Data
         self.default_data = {}
 
+        if self.app.is_legacy:
+            self.axes = None  # Matplotlib axes; usefull only in Legacy Mode
+
         self.kind = None  # Override with proper name
 
         # self.shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene)
@@ -331,7 +334,22 @@ class FlatCAMObj(QtCore.QObject):
         if self.deleted:
             return False
 
-        self.clear()
+        if self.app.is_legacy:
+            # 2D mode
+            # Axes must exist and be attached to canvas.
+            if self.axes is None or self.axes not in self.app.plotcanvas.figure.axes:
+                self.axes = self.app.plotcanvas.new_axes(self.options['name'])
+
+            if not self.options["plot"]:
+                self.axes.cla()
+                self.app.plotcanvas.auto_adjust_axes()
+                return False
+
+            # Clear axes or we will plot on top of them.
+            self.axes.cla()
+        else:
+            # 3D mode
+            self.clear()
         return True
 
     def single_object_plot(self):

+ 3 - 0
README.md

@@ -12,6 +12,9 @@ CAD program, and create G-Code for Isolation routing.
 20.09.2019
 
 - final fix for the --shellvar having spaces within the assigned value; now they are retained
+- legacy graphic engine - made the mouse events work (click, release, doubleclick, dragging)
+- legacy graphic engine - made the key events work (simple or with modifiers)
+- legacy graphic engine - made the mouse cursor work (enabled/disabled, position report); snapping is not moving the cursor yet
 
 19.09.2019
 

+ 5 - 1
camlib.py

@@ -117,7 +117,11 @@ class Geometry(object):
         self.old_disp_number = 0
         self.el_count = 0
 
-        self.temp_shapes = self.app.plotcanvas.new_shape_group()
+        if self.app.is_legacy is False:
+            self.temp_shapes = self.app.plotcanvas.new_shape_group()
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.temp_shapes = ShapeCollectionLegacy()
 
         # if geo_steps_per_circle is None:
         #     geo_steps_per_circle = int(Geometry.defaults["geo_steps_per_circle"])

+ 22 - 21
flatcamEditors/FlatCAMExcEditor.py

@@ -2012,8 +2012,14 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.exc_obj = None
 
         # VisPy Visuals
-        self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
-        self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        if self.app.is_legacy is False:
+            self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
+            self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.shapes = ShapeCollectionLegacy()
+            self.tool_shape = ShapeCollectionLegacy()
+
         self.app.pool_recreated.connect(self.pool_recreated)
 
         # Remove from scene
@@ -2791,16 +2797,16 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.canvas.vis_connect('mouse_press', self.on_canvas_click)
-        self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_exc_click_release)
+        self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_connect('mouse_release', self.on_exc_click_release)
 
         # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
         # but those from FlatCAMGeoEditor
-        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
         self.app.collection.view.clicked.disconnect()
 
         self.app.ui.popmenu_copy.triggered.disconnect()
@@ -2819,15 +2825,15 @@ class FlatCAMExcEditor(QtCore.QObject):
         # we restore the key and mouse control to FlatCAMApp method
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_double_click', self.app.on_double_click_over_plot)
         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_exc_click_release)
+        self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_disconnect('mouse_release', self.on_exc_click_release)
 
         try:
             self.app.ui.popmenu_copy.triggered.disconnect(self.exc_copy_drills)
@@ -3287,13 +3293,11 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         if self.app.grid_status() == True:
             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&nbsp;&nbsp;  <b>Dy</b>: "
@@ -3605,11 +3609,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         # ## Snap coordinates
         if self.app.grid_status() == True:
             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

+ 49 - 30
flatcamEditors/FlatCAMGeoEditor.py

@@ -3025,8 +3025,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
         # VisPy visuals
         self.fcgeometry = None
-        self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
-        self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        if self.app.is_legacy is False:
+            self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
+            self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.shapes = ShapeCollectionLegacy()
+            self.tool_shape = ShapeCollectionLegacy()
+
         self.app.pool_recreated.connect(self.pool_recreated)
 
         # Remove from scene
@@ -3271,7 +3277,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # Disable visuals
         self.shapes.enabled = False
         self.tool_shape.enabled = False
-        self.app.app_cursor.enabled = False
 
         self.app.ui.geo_editor_menu.setDisabled(True)
         self.app.ui.geo_editor_menu.menuAction().setVisible(False)
@@ -3309,16 +3314,16 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.canvas.vis_connect('mouse_press', self.on_canvas_click)
-        self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_geo_click_release)
+        self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_connect('mouse_release', self.on_geo_click_release)
 
         # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
         # but those from FlatCAMGeoEditor
-        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
 
         # self.app.collection.view.clicked.disconnect()
         self.app.ui.popmenu_copy.triggered.disconnect()
@@ -3354,15 +3359,15 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # we restore the key and mouse control to FlatCAMApp method
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.graph_event_connect('mouse_double_click', self.app.on_double_click_over_plot)
         # 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)
+        self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_disconnect('mouse_release', self.on_geo_click_release)
 
         try:
             self.app.ui.popmenu_copy.triggered.disconnect(lambda: self.select_tool('copy'))
@@ -3625,18 +3630,20 @@ class FlatCAMGeoEditor(QtCore.QObject):
         :param event: Event object dispatched by Matplotlib
         :return: None
         """
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+        else:
+            event_pos = (event.xdata, event.ydata)
 
-        self.pos = self.canvas.translate_coords(event.pos)
+        self.pos = self.canvas.translate_coords(event_pos)
 
         if self.app.grid_status() == True:
             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 == 1:
             self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
@@ -3678,7 +3685,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
         :param event: Event object dispatched by VisPy SceneCavas
         :return: None
         """
-        pos = self.canvas.translate_coords(event.pos)
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            event_is_dragging = event.is_dragging
+        else:
+            event_pos = (event.xdata, event.ydata)
+            event_is_dragging = self.app.plotcanvas.is_dragging
+
+        pos = self.canvas.translate_coords(event_pos)
         event.xdata, event.ydata = pos[0], pos[1]
 
         self.x = event.xdata
@@ -3688,7 +3702,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
         # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
         if event.button == 2:
-            if event.is_dragging:
+            if event_is_dragging:
                 self.app.ui.popMenu.mouse_is_panning = True
                 # return
             else:
@@ -3706,11 +3720,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # ### Snap coordinates ###
         if self.app.grid_status() == True:
             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
+            if self.app.is_legacy is False:
+                # Update cursor
+                self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
 
         self.snap_x = x
         self.snap_y = y
@@ -3728,7 +3740,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
                                            "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
 
-        if event.button == 1 and event.is_dragging and isinstance(self.active_tool, FCEraser):
+        if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
             pass
         else:
             # ### Utility geometry (animated) ###
@@ -3740,7 +3752,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
         # ### Selection area on canvas section ###
         dx = pos[0] - self.pos[0]
-        if event.is_dragging and event.button == 1:
+        if event_is_dragging and event.button == 1:
             self.app.delete_selection_shape()
             if dx < 0:
                 self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
@@ -3754,7 +3766,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.app.selection_type = None
 
     def on_geo_click_release(self, event):
-        pos_canvas = self.canvas.translate_coords(event.pos)
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            event_is_dragging = event.is_dragging
+        else:
+            event_pos = (event.xdata, event.ydata)
+            event_is_dragging = self.app.plotcanvas.is_dragging
+
+        pos_canvas = self.canvas.translate_coords(event_pos)
 
         if self.app.grid_status() == True:
             pos = self.snap(pos_canvas[0], pos_canvas[1])

+ 24 - 23
flatcamEditors/FlatCAMGrbEditor.py

@@ -2821,9 +2821,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.gerber_obj_options = dict()
 
         # VisPy Visuals
-        self.shapes = self.canvas.new_shape_collection(layers=1)
-        self.tool_shape = self.canvas.new_shape_collection(layers=1)
-        self.ma_annotation = self.canvas.new_text_group()
+        if self.app.is_legacy is False:
+            self.shapes = self.canvas.new_shape_collection(layers=1)
+            self.tool_shape = self.canvas.new_shape_collection(layers=1)
+            self.ma_annotation = self.canvas.new_text_group()
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.shapes = ShapeCollectionLegacy()
+            self.tool_shape = ShapeCollectionLegacy()
+            self.ma_annotation = ShapeCollectionLegacy()
 
         self.app.pool_recreated.connect(self.pool_recreated)
 
@@ -3511,14 +3517,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.canvas.vis_connect('mouse_press', self.on_canvas_click)
-        self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_grb_click_release)
-
-        self.canvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.canvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.canvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.canvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_connect('mouse_release', self.on_grb_click_release)
+
+        self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.canvas.graph_event_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
         self.app.collection.view.clicked.disconnect()
 
         self.app.ui.popmenu_copy.triggered.disconnect()
@@ -3550,15 +3556,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # we restore the key and mouse control to FlatCAMApp method
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
-        self.canvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.canvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.canvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.canvas.graph_event_connect('mouse_double_click', self.app.on_double_click_over_plot)
         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_grb_click_release)
+        self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
+        self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
+        self.canvas.graph_event_disconnect('mouse_release', self.on_grb_click_release)
 
         try:
             self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
@@ -4136,13 +4142,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         if self.app.grid_status() == True:
             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&nbsp;&nbsp;  <b>Dy</b>: "
@@ -4354,11 +4358,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # # ## Snap coordinates
         if self.app.grid_status() == True:
             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

+ 28 - 1
flatcamGUI/FlatCAMGUI.py

@@ -12,6 +12,7 @@
 # ##########################################################
 
 from flatcamGUI.PreferencesUI import *
+from matplotlib.backend_bases import KeyEvent as mpl_key_event
 
 import gettext
 import FlatCAMTranslation as fcTranslate
@@ -2271,6 +2272,32 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         # events from the GUI are of type QKeyEvent
         elif type(event) == QtGui.QKeyEvent:
             key = event.key()
+        elif isinstance(event, mpl_key_event):
+            key = event.key
+            # if modifiers == QtCore.Qt.NoModifier:
+            try:
+                key = ord(key.upper())
+            except TypeError:
+                key = key.upper()
+                if 'ctrl' in key.lower():
+                    modifiers = QtCore.Qt.ControlModifier
+                    try:
+                        key = ord(key.rpartition('+')[2].upper())
+                    except TypeError:
+                        pass
+                elif 'alt' in key.lower():
+                    modifiers = QtCore.Qt.AltModifier
+                    try:
+                        key = ord(key.rpartition('+')[2].upper())
+                    except TypeError:
+                        pass
+                elif 'shift' in key.lower():
+                    modifiers = QtCore.Qt.ShiftModifier
+                    try:
+                        key = ord(key.rpartition('+')[2].upper())
+                    except TypeError:
+                        pass
+
         # events from Vispy are of type KeyEvent
         else:
             key = event.key
@@ -2494,7 +2521,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
                     # try to disconnect the slot from Set Origin
                     try:
-                        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_set_zero_click)
+                        self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_set_zero_click)
                     except TypeError:
                         pass
                     self.app.inform.emit("")

+ 2 - 2
flatcamGUI/PlotCanvas.py

@@ -152,10 +152,10 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         except Exception as e:
             pass
 
-    def vis_connect(self, event_name, callback):
+    def graph_event_connect(self, event_name, callback):
         return getattr(self.events, event_name).connect(callback)
 
-    def vis_disconnect(self, event_name, callback=None):
+    def graph_event_disconnect(self, event_name, callback=None):
         if callback is None:
             getattr(self.events, event_name).disconnect()
         else:

+ 637 - 0
flatcamGUI/PlotCanvasLegacy.py

@@ -0,0 +1,637 @@
+############################################################
+# FlatCAM: 2D Post-processing for Manufacturing            #
+# http://caram.cl/software/flatcam                         #
+# Author: Juan Pablo Caram (c)                             #
+# Date: 2/5/2014                                           #
+# MIT Licence                                              #
+############################################################
+
+from PyQt5 import QtGui, QtCore, QtWidgets
+
+# Prevent conflict with Qt5 and above.
+from matplotlib import use as mpl_use
+
+from matplotlib.figure import Figure
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.backends.backend_agg import FigureCanvasAgg
+from matplotlib.widgets import Cursor
+
+import FlatCAMApp
+import logging
+
+mpl_use("Qt5Agg")
+log = logging.getLogger('base')
+
+
+class CanvasCache(QtCore.QObject):
+    """
+
+    Case story #1:
+
+    1) No objects in the project.
+    2) Object is created (new_object() emits object_created(obj)).
+       on_object_created() adds (i) object to collection and emits
+       (ii) new_object_available() then calls (iii) object.plot()
+    3) object.plot() creates axes if necessary on
+       app.collection.figure. Then plots on it.
+    4) Plots on a cache-size canvas (in background).
+    5) Plot completes. Bitmap is generated.
+    6) Visible canvas is painted.
+
+    """
+
+    # Signals:
+    # A bitmap is ready to be displayed.
+    new_screen = QtCore.pyqtSignal()
+
+    def __init__(self, plotcanvas, app, dpi=50):
+
+        super(CanvasCache, self).__init__()
+
+        self.app = app
+
+        self.plotcanvas = plotcanvas
+        self.dpi = dpi
+
+        self.figure = Figure(dpi=dpi)
+
+        self.axes = self.figure.add_axes([0.0, 0.0, 1.0, 1.0], alpha=1.0)
+        self.axes.set_frame_on(False)
+        self.axes.set_xticks([])
+        self.axes.set_yticks([])
+
+        self.canvas = FigureCanvasAgg(self.figure)
+
+        self.cache = None
+
+    def run(self):
+
+        log.debug("CanvasCache Thread Started!")
+        self.plotcanvas.update_screen_request.connect(self.on_update_req)
+
+    def on_update_req(self, extents):
+        """
+        Event handler for an updated display request.
+
+        :param extents: [xmin, xmax, ymin, ymax, zoom(optional)]
+        """
+
+        # log.debug("Canvas update requested: %s" % str(extents))
+
+        # Note: This information below might be out of date. Establish
+        # a protocol regarding when to change the canvas in the main
+        # thread and when to check these values here in the background,
+        # or pass this data in the signal (safer).
+        # log.debug("Size: %s [px]" % str(self.plotcanvas.get_axes_pixelsize()))
+        # log.debug("Density: %s [units/px]" % str(self.plotcanvas.get_density()))
+
+        # Move the requested screen portion to the main thread
+        # and inform about the update:
+
+        self.new_screen.emit()
+
+        # Continue to update the cache.
+
+    # def on_new_object_available(self):
+    #
+    #     log.debug("A new object is available. Should plot it!")
+
+
+class PlotCanvasLegacy(QtCore.QObject):
+    """
+    Class handling the plotting area in the application.
+    """
+
+    # Signals:
+    # Request for new bitmap to display. The parameter
+    # is a list with [xmin, xmax, ymin, ymax, zoom(optional)]
+    update_screen_request = QtCore.pyqtSignal(list)
+    double_click = QtCore.pyqtSignal(object)
+
+    def __init__(self, container, app):
+        """
+        The constructor configures the Matplotlib figure that
+        will contain all plots, creates the base axes and connects
+        events to the plotting area.
+
+        :param container: The parent container in which to draw plots.
+        :rtype: PlotCanvas
+        """
+
+        super(PlotCanvasLegacy, self).__init__()
+
+        self.app = app
+
+        # Options
+        self.x_margin = 15  # pixels
+        self.y_margin = 25  # Pixels
+
+        # Parent container
+        self.container = container
+
+        # Plots go onto a single matplotlib.figure
+        self.figure = Figure(dpi=50)  # TODO: dpi needed?
+        self.figure.patch.set_visible(False)
+
+        # These axes show the ticks and grid. No plotting done here.
+        # New axes must have a label, otherwise mpl returns an existing one.
+        self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label="base", alpha=0.0)
+        self.axes.set_aspect(1)
+        self.axes.grid(True)
+        self.axes.axhline(color='Black')
+        self.axes.axvline(color='Black')
+
+        # The canvas is the top level container (FigureCanvasQTAgg)
+        self.canvas = FigureCanvas(self.figure)
+        self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
+        self.canvas.setFocus()
+        self.native = self.canvas
+
+        # self.canvas.set_hexpand(1)
+        # self.canvas.set_vexpand(1)
+        # self.canvas.set_can_focus(True)  # For key press
+
+        # Attach to parent
+        # self.container.attach(self.canvas, 0, 0, 600, 400)  # TODO: Height and width are num. columns??
+        self.container.addWidget(self.canvas)  # Qt
+
+        # Copy a bitmap of the canvas for quick animation.
+        # Update every time the canvas is re-drawn.
+        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+
+        # ## Bitmap Cache
+        self.cache = CanvasCache(self, self.app)
+        self.cache_thread = QtCore.QThread()
+        self.cache.moveToThread(self.cache_thread)
+        # super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
+        self.cache_thread.started.connect(self.cache.run)
+
+        self.cache_thread.start()
+        self.cache.new_screen.connect(self.on_new_screen)
+
+        # Events
+        self.graph_event_connect('button_press_event', self.on_mouse_press)
+        self.graph_event_connect('button_release_event', self.on_mouse_release)
+        self.graph_event_connect('motion_notify_event', self.on_mouse_move)
+        # self.canvas.connect('configure-event', self.auto_adjust_axes)
+        self.graph_event_connect('resize_event', self.auto_adjust_axes)
+        # self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
+        # self.canvas.connect("scroll-event", self.on_scroll)
+        self.graph_event_connect('scroll_event', self.on_scroll)
+        # self.graph_event_connect('key_press_event', self.on_key_down)
+        # self.graph_event_connect('key_release_event', self.on_key_up)
+        self.graph_event_connect('draw_event', self.on_draw)
+
+        self.mouse = [0, 0]
+        self.key = None
+
+        self.pan_axes = []
+        self.panning = False
+
+        # signal is the mouse is dragging
+        self.is_dragging = False
+
+        # signal if there is a doubleclick
+        self.is_dblclk = False
+
+    def graph_event_connect(self, event_name, callback):
+        """
+        Attach an event handler to the canvas through the Matplotlib interface.
+
+        :param event_name: Name of the event
+        :type event_name: str
+        :param callback: Function to call
+        :type callback: func
+        :return: Connection id
+        :rtype: int
+        """
+        if event_name == 'mouse_move':
+            event_name = 'motion_notify_event'
+        if event_name == 'mouse_press':
+            event_name = 'button_press_event'
+        if event_name == 'mouse_release':
+            event_name = 'button_release_event'
+        if event_name == 'mouse_double_click':
+            return self.double_click.connect(callback)
+
+        if event_name == 'key_press':
+            event_name = 'key_press_event'
+
+        return self.canvas.mpl_connect(event_name, callback)
+
+    def graph_event_disconnect(self, cid):
+        """
+        Disconnect callback with the give id.
+        :param cid: Callback id.
+        :return: None
+        """
+        if cid == 'mouse_move':
+            cid = 'motion_notify_event'
+        if cid == 'mouse_press':
+            cid = 'button_press_event'
+        if cid == 'mouse_release':
+            cid = 'button_release_event'
+        if cid == 'mouse_double_click':
+            self.double_click.disconnect(cid)
+            return
+
+        if cid == 'key_press':
+            cid = 'key_press_event'
+
+        self.canvas.mpl_disconnect(cid)
+
+    def on_new_screen(self):
+        pass
+        # log.debug("Cache updated the screen!")
+
+    def new_cursor(self):
+        c = MplCursor(axes=self.axes, color='black', linewidth=1)
+        return c
+
+    def on_key_down(self, event):
+        """
+
+        :param event:
+        :return:
+        """
+        FlatCAMApp.App.log.debug('on_key_down(): ' + str(event.key))
+        self.key = event.key
+
+    def on_key_up(self, event):
+        """
+
+        :param event:
+        :return:
+        """
+        self.key = None
+
+    def connect(self, event_name, callback):
+        """
+        Attach an event handler to the canvas through the native Qt interface.
+
+        :param event_name: Name of the event
+        :type event_name: str
+        :param callback: Function to call
+        :type callback: function
+        :return: Nothing
+        """
+        self.canvas.connect(event_name, callback)
+
+    def clear(self):
+        """
+        Clears axes and figure.
+
+        :return: None
+        """
+
+        # Clear
+        self.axes.cla()
+        try:
+            self.figure.clf()
+        except KeyError:
+            FlatCAMApp.App.log.warning("KeyError in MPL figure.clf()")
+
+        # Re-build
+        self.figure.add_axes(self.axes)
+        self.axes.set_aspect(1)
+        self.axes.grid(True)
+
+        # Re-draw
+        self.canvas.draw_idle()
+
+    def adjust_axes(self, xmin, ymin, xmax, ymax):
+        """
+        Adjusts all axes while maintaining the use of the whole canvas
+        and an aspect ratio to 1:1 between x and y axes. The parameters are an original
+        request that will be modified to fit these restrictions.
+
+        :param xmin: Requested minimum value for the X axis.
+        :type xmin: float
+        :param ymin: Requested minimum value for the Y axis.
+        :type ymin: float
+        :param xmax: Requested maximum value for the X axis.
+        :type xmax: float
+        :param ymax: Requested maximum value for the Y axis.
+        :type ymax: float
+        :return: None
+        """
+
+        # FlatCAMApp.App.log.debug("PC.adjust_axes()")
+
+        width = xmax - xmin
+        height = ymax - ymin
+        try:
+            r = width / height
+        except ZeroDivisionError:
+            FlatCAMApp.App.log.error("Height is %f" % height)
+            return
+        canvas_w, canvas_h = self.canvas.get_width_height()
+        canvas_r = float(canvas_w) / canvas_h
+        x_ratio = float(self.x_margin) / canvas_w
+        y_ratio = float(self.y_margin) / canvas_h
+
+        if r > canvas_r:
+            ycenter = (ymin + ymax) / 2.0
+            newheight = height * r / canvas_r
+            ymin = ycenter - newheight / 2.0
+            ymax = ycenter + newheight / 2.0
+        else:
+            xcenter = (xmax + xmin) / 2.0
+            newwidth = width * canvas_r / r
+            xmin = xcenter - newwidth / 2.0
+            xmax = xcenter + newwidth / 2.0
+
+        # Adjust axes
+        for ax in self.figure.get_axes():
+            if ax._label != 'base':
+                ax.set_frame_on(False)  # No frame
+                ax.set_xticks([])  # No tick
+                ax.set_yticks([])  # No ticks
+                ax.patch.set_visible(False)  # No background
+                ax.set_aspect(1)
+            ax.set_xlim((xmin, xmax))
+            ax.set_ylim((ymin, ymax))
+            ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])
+
+        # Sync re-draw to proper paint on form resize
+        self.canvas.draw()
+
+        # #### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
+
+    def auto_adjust_axes(self, *args):
+        """
+        Calls ``adjust_axes()`` using the extents of the base axes.
+
+        :rtype : None
+        :return: None
+        """
+
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        self.adjust_axes(xmin, ymin, xmax, ymax)
+
+    def zoom(self, factor, center=None):
+        """
+        Zooms the plot by factor around a given
+        center point. Takes care of re-drawing.
+
+        :param factor: Number by which to scale the plot.
+        :type factor: float
+        :param center: Coordinates [x, y] of the point around which to scale the plot.
+        :type center: list
+        :return: None
+        """
+
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        width = xmax - xmin
+        height = ymax - ymin
+
+        if center is None or center == [None, None]:
+            center = [(xmin + xmax) / 2.0, (ymin + ymax) / 2.0]
+
+        # For keeping the point at the pointer location
+        relx = (xmax - center[0]) / width
+        rely = (ymax - center[1]) / height
+
+        new_width = width / factor
+        new_height = height / factor
+
+        xmin = center[0] - new_width * (1 - relx)
+        xmax = center[0] + new_width * relx
+        ymin = center[1] - new_height * (1 - rely)
+        ymax = center[1] + new_height * rely
+
+        # Adjust axes
+        for ax in self.figure.get_axes():
+            ax.set_xlim((xmin, xmax))
+            ax.set_ylim((ymin, ymax))
+
+        # Async re-draw
+        self.canvas.draw_idle()
+
+        # #### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
+
+    def pan(self, x, y):
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        width = xmax - xmin
+        height = ymax - ymin
+
+        # Adjust axes
+        for ax in self.figure.get_axes():
+            ax.set_xlim((xmin + x * width, xmax + x * width))
+            ax.set_ylim((ymin + y * height, ymax + y * height))
+
+        # Re-draw
+        self.canvas.draw_idle()
+
+        # #### Temporary place-holder for cached update #####
+        self.update_screen_request.emit([0, 0, 0, 0, 0])
+
+    def new_axes(self, name):
+        """
+        Creates and returns an Axes object attached to this object's Figure.
+
+        :param name: Unique label for the axes.
+        :return: Axes attached to the figure.
+        :rtype: Axes
+        """
+
+        return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
+
+    def on_scroll(self, event):
+        """
+        Scroll event handler.
+
+        :param event: Event object containing the event information.
+        :return: None
+        """
+
+        # So it can receive key presses
+        # self.canvas.grab_focus()
+        self.canvas.setFocus()
+
+        # Event info
+        # z, direction = event.get_scroll_direction()
+
+        if self.key is None:
+
+            if event.button == 'up':
+                self.zoom(1.5, self.mouse)
+            else:
+                self.zoom(1 / 1.5, self.mouse)
+            return
+
+        if self.key == 'shift':
+
+            if event.button == 'up':
+                self.pan(0.3, 0)
+            else:
+                self.pan(-0.3, 0)
+            return
+
+        if self.key == 'control':
+
+            if event.button == 'up':
+                self.pan(0, 0.3)
+            else:
+                self.pan(0, -0.3)
+            return
+
+    def on_mouse_press(self, event):
+
+        self.is_dragging = True
+
+        # Check for middle mouse button press
+        if self.app.defaults["global_pan_button"] == '2':
+            pan_button = 3  # right button for Matplotlib
+        else:
+            pan_button = 2  # middle button for Matplotlib
+
+        if event.button == pan_button:
+            # Prepare axes for pan (using 'matplotlib' pan function)
+            self.pan_axes = []
+            for a in self.figure.get_axes():
+                if (event.x is not None and event.y is not None and a.in_axes(event) and
+                        a.get_navigate() and a.can_pan()):
+                    a.start_pan(event.x, event.y, 1)
+                    self.pan_axes.append(a)
+
+            # Set pan view flag
+            if len(self.pan_axes) > 0:
+                self.panning = True
+
+        if event.dblclick:
+            self.double_click.emit(event)
+
+    def on_mouse_release(self, event):
+
+        self.is_dragging = False
+
+        # Check for middle mouse button release to complete pan procedure
+        # Check for middle mouse button press
+        if self.app.defaults["global_pan_button"] == '2':
+            pan_button = 3  # right button for Matplotlib
+        else:
+            pan_button = 2  # middle button for Matplotlib
+
+        if event.button == pan_button:
+            for a in self.pan_axes:
+                a.end_pan()
+
+            # Clear pan flag
+            self.panning = False
+
+    def on_mouse_move(self, event):
+        """
+        Mouse movement event hadler. Stores the coordinates. Updates view on pan.
+
+        :param event: Contains information about the event.
+        :return: None
+        """
+        self.mouse = [event.xdata, event.ydata]
+
+        # Update pan view on mouse move
+        if self.panning is True:
+            for a in self.pan_axes:
+                a.drag_pan(1, event.key, event.x, event.y)
+
+            # Async re-draw (redraws only on thread idle state, uses timer on backend)
+            self.canvas.draw_idle()
+
+            # #### Temporary place-holder for cached update #####
+            self.update_screen_request.emit([0, 0, 0, 0, 0])
+
+    def translate_coords(self, position):
+        """
+        This does not do much. It's just for code compatibility
+
+        :param position: Mouse event position
+        :return: Tuple with mouse position
+        """
+        return (position[0], position[1])
+
+    def on_draw(self, renderer):
+
+        # Store background on canvas redraw
+        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
+
+    def get_axes_pixelsize(self):
+        """
+        Axes size in pixels.
+
+        :return: Pixel width and height
+        :rtype: tuple
+        """
+        bbox = self.axes.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
+        width, height = bbox.width, bbox.height
+        width *= self.figure.dpi
+        height *= self.figure.dpi
+        return width, height
+
+    def get_density(self):
+        """
+        Returns unit length per pixel on horizontal
+        and vertical axes.
+
+        :return: X and Y density
+        :rtype: tuple
+        """
+        xpx, ypx = self.get_axes_pixelsize()
+
+        xmin, xmax = self.axes.get_xlim()
+        ymin, ymax = self.axes.get_ylim()
+        width = xmax - xmin
+        height = ymax - ymin
+
+        return width / xpx, height / ypx
+
+
+class MplCursor():
+
+    def __init__(self, axes, color='red', linewidth=1):
+        self._enabled = True
+
+        self.axes = axes
+        self.color = color
+        self.linewidth = linewidth
+        self.cursor = Cursor(self.axes, useblit=True, color=self.color, linewidth=self.linewidth)
+
+    @property
+    def enabled(self):
+        return True if self._enabled else False
+
+    @enabled.setter
+    def enabled(self, value):
+        self._enabled = value
+        self.cursor.visible = self._enabled
+        self.cursor.canvas.draw()
+
+    def set_data(self, pos):
+        self.cursor.linev.set_xdata((pos[0], pos[0]))
+        self.cursor.lineh.set_ydata(([pos[1]], pos[1]))
+
+
+class ShapeCollectionLegacy():
+
+    def __init__(self):
+        self._shapes = []
+
+    def add(self, shape):
+        try:
+            for sh in shape:
+                self._shapes.append(sh)
+        except TypeError:
+            self._shapes.append(shape)
+
+    def clear(self, update=None):
+        self._shapes[:] = []
+
+        if update is True:
+            self.redraw()
+
+    def redraw(self):
+        pass

+ 12 - 0
flatcamGUI/PreferencesUI.py

@@ -846,6 +846,17 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         self.units_radio = RadioSet([{'label': _('IN'), 'value': 'IN'},
                                      {'label': _('MM'), 'value': 'MM'}])
 
+        # Graphic Engine for FlatCAM
+        self.ge_label = QtWidgets.QLabel('<b>%s:</b>' % _('Graphic Engine'))
+        self.ge_label.setToolTip(_("Choose what graphic engine to use in FlatCAM.\n"
+                                   "Legacy(2D) -> reduced functionality, slow performance but enhanced compatibility.\n"
+                                   "OpenGL(3D) -> full functionality, high performance\n"
+                                   "Some graphic cards are too old and do not work in OpenGL(3D) mode, like:\n"
+                                   "Intel HD3000 or older. In this case the plot area will be black therefore\n"
+                                   "use the Legacy(2D) mode."))
+        self.ge_radio = RadioSet([{'label': _('Legacy(2D)'), 'value': '2D'},
+                                  {'label': _('OpenGL(3D)'), 'value': '3D'}])
+
         # Application Level for FlatCAM
         self.app_level_label = QtWidgets.QLabel('<b>%s:</b>' % _('APP. LEVEL'))
         self.app_level_label.setToolTip(_("Choose the default level of usage for FlatCAM.\n"
@@ -963,6 +974,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
 
         # Add (label - input field) pair to the QFormLayout
         self.form_box.addRow(self.unitslabel, self.units_radio)
+        self.form_box.addRow(self.ge_label, self.ge_radio)
         self.form_box.addRow(self.app_level_label, self.app_level_radio)
         self.form_box.addRow(self.portability_label, self.portability_cb)
         self.form_box.addRow(QtWidgets.QLabel(''))

+ 28 - 28
flatcamTools/ToolCutOut.py

@@ -780,13 +780,13 @@ class CutOut(FlatCAMTool):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name))
             return "Could not retrieve object: %s" % name
 
-        self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
-        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.plotcanvas.vis_connect('key_press', self.on_key_press)
-        self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move)
-        self.app.plotcanvas.vis_connect('mouse_release', self.on_mouse_click_release)
+        self.app.plotcanvas.graph_event_disconnect('key_press', self.app.ui.keyPressEvent)
+        self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
+        self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
+        self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
 
     def on_manual_cutout(self, click_pos):
         name = self.man_object_combo.currentText()
@@ -929,13 +929,13 @@ class CutOut(FlatCAMTool):
             pos = self.app.plotcanvas.translate_coords(event.pos)
             self.on_manual_cutout(click_pos=pos)
 
-            # self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
-            # self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
-            # self.app.plotcanvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
-            # self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
-            # self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-            # self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            # self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+            # self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
+            # self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+            # self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
+            # self.app.plotcanvas.graph_event_connect('key_press', self.app.ui.keyPressEvent)
+            # self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+            # self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            # self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
 
             # self.app.geo_editor.tool_shape.clear(update=True)
             # self.app.geo_editor.tool_shape.enabled = False
@@ -943,13 +943,13 @@ class CutOut(FlatCAMTool):
 
         # if RMB then we exit
         elif event.button == 2 and self.mouse_is_dragging is False:
-            self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
-            self.app.plotcanvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
-            self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
-            self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
+            self.app.plotcanvas.graph_event_connect('key_press', self.app.ui.keyPressEvent)
+            self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
 
             # Remove any previous utility shape
             self.app.geo_editor.tool_shape.clear(update=True)
@@ -1064,13 +1064,13 @@ class CutOut(FlatCAMTool):
 
         # Escape = Deselect All
         if key == QtCore.Qt.Key_Escape or key == 'Escape':
-            self.app.plotcanvas.vis_disconnect('key_press', self.on_key_press)
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move)
-            self.app.plotcanvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
-            self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
-            self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
+            self.app.plotcanvas.graph_event_connect('key_press', self.app.ui.keyPressEvent)
+            self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
 
             # Remove any previous utility shape
             self.app.geo_editor.tool_shape.clear(update=True)

+ 33 - 31
flatcamTools/ToolMeasurement.py

@@ -113,7 +113,11 @@ class Measurement(FlatCAMTool):
         self.original_call_source = 'app'
 
         # VisPy visuals
-        self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
+        if self.app.is_legacy is False:
+            self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.sel_shapes = ShapeCollectionLegacy()
 
         self.measure_btn.clicked.connect(self.activate_measure_tool)
 
@@ -178,26 +182,26 @@ class Measurement(FlatCAMTool):
 
         # we can connect the app mouse events to the measurement tool
         # NEVER DISCONNECT THOSE before connecting some other handlers; it breaks something in VisPy
-        self.canvas.vis_connect('mouse_move', self.on_mouse_move_meas)
-        self.canvas.vis_connect('mouse_release', self.on_mouse_click_release)
+        self.canvas.graph_event_connect('mouse_move', self.on_mouse_move_meas)
+        self.canvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
 
         # we disconnect the mouse/key handlers from wherever the measurement tool was called
         if self.app.call_source == 'app':
-            self.canvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.canvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.canvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
         elif self.app.call_source == 'geo_editor':
-            self.canvas.vis_disconnect('mouse_move', self.app.geo_editor.on_canvas_move)
-            self.canvas.vis_disconnect('mouse_press', self.app.geo_editor.on_canvas_click)
-            self.canvas.vis_disconnect('mouse_release', self.app.geo_editor.on_geo_click_release)
+            self.canvas.graph_event_disconnect('mouse_move', self.app.geo_editor.on_canvas_move)
+            self.canvas.graph_event_disconnect('mouse_press', self.app.geo_editor.on_canvas_click)
+            self.canvas.graph_event_disconnect('mouse_release', self.app.geo_editor.on_geo_click_release)
         elif self.app.call_source == 'exc_editor':
-            self.canvas.vis_disconnect('mouse_move', self.app.exc_editor.on_canvas_move)
-            self.canvas.vis_disconnect('mouse_press', self.app.exc_editor.on_canvas_click)
-            self.canvas.vis_disconnect('mouse_release', self.app.exc_editor.on_exc_click_release)
+            self.canvas.graph_event_disconnect('mouse_move', self.app.exc_editor.on_canvas_move)
+            self.canvas.graph_event_disconnect('mouse_press', self.app.exc_editor.on_canvas_click)
+            self.canvas.graph_event_disconnect('mouse_release', self.app.exc_editor.on_exc_click_release)
         elif self.app.call_source == 'grb_editor':
-            self.canvas.vis_disconnect('mouse_move', self.app.grb_editor.on_canvas_move)
-            self.canvas.vis_disconnect('mouse_press', self.app.grb_editor.on_canvas_click)
-            self.canvas.vis_disconnect('mouse_release', self.app.grb_editor.on_grb_click_release)
+            self.canvas.graph_event_disconnect('mouse_move', self.app.grb_editor.on_canvas_move)
+            self.canvas.graph_event_disconnect('mouse_press', self.app.grb_editor.on_canvas_click)
+            self.canvas.graph_event_disconnect('mouse_release', self.app.grb_editor.on_grb_click_release)
 
         self.app.call_source = 'measurement'
 
@@ -210,25 +214,25 @@ class Measurement(FlatCAMTool):
 
         self.app.call_source = copy(self.original_call_source)
         if self.original_call_source == 'app':
-            self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.canvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.canvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
         elif self.original_call_source == 'geo_editor':
-            self.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
-            self.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
-            self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_geo_click_release)
+            self.canvas.graph_event_connect('mouse_move', self.app.geo_editor.on_canvas_move)
+            self.canvas.graph_event_connect('mouse_press', self.app.geo_editor.on_canvas_click)
+            self.canvas.graph_event_connect('mouse_release', self.app.geo_editor.on_geo_click_release)
         elif self.original_call_source == 'exc_editor':
-            self.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
-            self.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
-            self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_exc_click_release)
+            self.canvas.graph_event_connect('mouse_move', self.app.exc_editor.on_canvas_move)
+            self.canvas.graph_event_connect('mouse_press', self.app.exc_editor.on_canvas_click)
+            self.canvas.graph_event_connect('mouse_release', self.app.exc_editor.on_exc_click_release)
         elif self.original_call_source == 'grb_editor':
-            self.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move)
-            self.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click)
-            self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_grb_click_release)
+            self.canvas.graph_event_connect('mouse_move', self.app.grb_editor.on_canvas_move)
+            self.canvas.graph_event_connect('mouse_press', self.app.grb_editor.on_canvas_click)
+            self.canvas.graph_event_connect('mouse_release', self.app.grb_editor.on_grb_click_release)
 
         # disconnect the mouse/key events from functions of measurement tool
-        self.canvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
-        self.canvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
+        self.canvas.graph_event_disconnect('mouse_move', self.on_mouse_move_meas)
+        self.canvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
 
         # self.app.ui.notebook.setTabText(2, _("Tools"))
         # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
@@ -289,13 +293,11 @@ class Measurement(FlatCAMTool):
             pos_canvas = self.app.plotcanvas.translate_coords(event.pos)
             if self.app.grid_status() == True:
                 pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
-                self.app.app_cursor.enabled = True
                 # Update cursor
                 self.app.app_cursor.set_data(np.asarray([(pos[0], pos[1])]),
                                              symbol='++', edge_color='black', size=20)
             else:
                 pos = (pos_canvas[0], pos_canvas[1])
-                self.app.app_cursor.enabled = False
 
             if self.rel_point1 is not None:
                 dx = pos[0] - self.rel_point1[0]

+ 12 - 8
flatcamTools/ToolMove.py

@@ -44,7 +44,11 @@ class ToolMove(FlatCAMTool):
         self.old_coords = []
 
         # VisPy visuals
-        self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
+        if self.app.is_legacy is False:
+            self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
+        else:
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.sel_shapes = ShapeCollectionLegacy()
 
         self.replot_signal[list].connect(self.replot)
 
@@ -62,10 +66,10 @@ class ToolMove(FlatCAMTool):
         if self.isVisible():
             self.setVisible(False)
 
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.on_move)
-            self.app.plotcanvas.vis_disconnect('mouse_press', self.on_left_click)
-            self.app.plotcanvas.vis_disconnect('key_release', self.on_key_press)
-            self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_move)
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.on_left_click)
+            self.app.plotcanvas.graph_event_disconnect('key_release', self.on_key_press)
+            self.app.plotcanvas.graph_event_connect('key_press', self.app.ui.keyPressEvent)
 
             self.clicked_move = 0
 
@@ -228,9 +232,9 @@ class ToolMove(FlatCAMTool):
             self.toggle()
         else:
             # if we have an object selected then we can safely activate the mouse events
-            self.app.plotcanvas.vis_connect('mouse_move', self.on_move)
-            self.app.plotcanvas.vis_connect('mouse_press', self.on_left_click)
-            self.app.plotcanvas.vis_connect('key_release', self.on_key_press)
+            self.app.plotcanvas.graph_event_connect('mouse_move', self.on_move)
+            self.app.plotcanvas.graph_event_connect('mouse_press', self.on_left_click)
+            self.app.plotcanvas.graph_event_connect('key_release', self.on_key_press)
             # first get a bounding box to fit all
             for obj in obj_list:
                 xmin, ymin, xmax, ymax = obj.bounds()

+ 15 - 17
flatcamTools/ToolNonCopperClear.py

@@ -1197,21 +1197,21 @@ class NonCopperClear(FlatCAMTool, Gerber):
                         #                   contour=contour,
                         #                   rest=rest)
                         #
-                        # self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
-                        # self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
+                        # self.app.plotcanvas.graph_event_disconnect('mouse_release', on_mouse_release)
+                        # self.app.plotcanvas.graph_event_disconnect('mouse_move', on_mouse_move)
                         #
-                        # self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                        # self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-                        # self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
                 elif event.button == 2 and self.mouse_is_dragging == False:
                     self.first_click = False
 
-                    self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
-                    self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
+                    self.app.plotcanvas.graph_event_disconnect('mouse_release', on_mouse_release)
+                    self.app.plotcanvas.graph_event_disconnect('mouse_move', on_mouse_move)
 
-                    self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                    self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-                    self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
 
                     if len(self.sel_rect) == 0:
                         return
@@ -1231,7 +1231,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
             # called on mouse move
             def on_mouse_move(event):
                 curr_pos = self.app.plotcanvas.translate_coords(event.pos)
-                self.app.app_cursor.enabled = False
 
                 # detect mouse dragging motion
                 if event.is_dragging is True:
@@ -1241,7 +1240,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
                 # update the cursor position
                 if self.app.grid_status() == True:
-                    self.app.app_cursor.enabled = True
                     # Update cursor
                     curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
                     self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
@@ -1254,12 +1252,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
                                                          coords=(curr_pos[0], curr_pos[1]),
                                                          face_alpha=0.0)
 
-            self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
 
-            self.app.plotcanvas.vis_connect('mouse_release', on_mouse_release)
-            self.app.plotcanvas.vis_connect('mouse_move', on_mouse_move)
+            self.app.plotcanvas.graph_event_connect('mouse_release', on_mouse_release)
+            self.app.plotcanvas.graph_event_connect('mouse_move', on_mouse_move)
         elif select_method == 'box':
             self.bound_obj_name = self.box_combo.currentText()
             # Get source object.

+ 21 - 23
flatcamTools/ToolPaint.py

@@ -1005,7 +1005,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                 # do paint single only for left mouse clicks
                 if event.button == 1:
                     self.app.inform.emit(_("Painting polygon..."))
-                    self.app.plotcanvas.vis_disconnect('mouse_press', doit)
+                    self.app.plotcanvas.graph_event_disconnect('mouse_press', doit)
 
                     pos = self.app.plotcanvas.translate_coords(event.pos)
                     if self.app.grid_status() == True:
@@ -1017,12 +1017,12 @@ class ToolPaint(FlatCAMTool, Gerber):
                                     overlap=overlap,
                                     connect=connect,
                                     contour=contour)
-                    self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                    self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
 
-            self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.app.plotcanvas.vis_connect('mouse_press', doit)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_connect('mouse_press', doit)
 
         elif select_method == "area":
             self.app.inform.emit('[WARNING_NOTCL] %s' %
@@ -1082,21 +1082,21 @@ class ToolPaint(FlatCAMTool, Gerber):
                         #                      connect=connect,
                         #                      contour=contour)
                         #
-                        # self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
-                        # self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
+                        # self.app.plotcanvas.graph_event_disconnect('mouse_release', on_mouse_release)
+                        # self.app.plotcanvas.graph_event_disconnect('mouse_move', on_mouse_move)
                         #
-                        # self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                        # self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-                        # self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+                        # self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
                 elif event.button == 2 and self.mouse_is_dragging is False:
                     self.first_click = False
 
-                    self.app.plotcanvas.vis_disconnect('mouse_release', on_mouse_release)
-                    self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
+                    self.app.plotcanvas.graph_event_disconnect('mouse_release', on_mouse_release)
+                    self.app.plotcanvas.graph_event_disconnect('mouse_move', on_mouse_move)
 
-                    self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                    self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-                    self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+                    self.app.plotcanvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
 
                     if len(self.sel_rect) == 0:
                         return
@@ -1113,7 +1113,6 @@ class ToolPaint(FlatCAMTool, Gerber):
             # called on mouse move
             def on_mouse_move(event):
                 curr_pos = self.app.plotcanvas.translate_coords(event.pos)
-                self.app.app_cursor.enabled = False
 
                 # detect mouse dragging motion
                 if event.is_dragging is True:
@@ -1123,7 +1122,6 @@ class ToolPaint(FlatCAMTool, Gerber):
 
                 # update the cursor position
                 if self.app.grid_status() == True:
-                    self.app.app_cursor.enabled = True
                     # Update cursor
                     curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
                     self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
@@ -1136,12 +1134,12 @@ class ToolPaint(FlatCAMTool, Gerber):
                                                          coords=(curr_pos[0], curr_pos[1]),
                                                          face_alpha=0.0)
 
-            self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
 
-            self.app.plotcanvas.vis_connect('mouse_release', on_mouse_release)
-            self.app.plotcanvas.vis_connect('mouse_move', on_mouse_move)
+            self.app.plotcanvas.graph_event_connect('mouse_release', on_mouse_release)
+            self.app.plotcanvas.graph_event_connect('mouse_move', on_mouse_move)
 
         elif select_method == 'ref':
             self.bound_obj_name = self.box_combo.currentText()