|
|
@@ -10,14 +10,6 @@
|
|
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
|
from PyQt5.QtCore import pyqtSignal
|
|
|
|
|
|
-# Prevent conflict with Qt5 and above.
|
|
|
-from matplotlib import use as mpl_use
|
|
|
-mpl_use("Qt5Agg")
|
|
|
-from matplotlib.figure import Figure
|
|
|
-from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
|
|
|
-from matplotlib.backends.backend_agg import FigureCanvasAgg
|
|
|
-from matplotlib.widgets import Cursor
|
|
|
-
|
|
|
# needed for legacy mode
|
|
|
# Used for solid polygons in Matplotlib
|
|
|
from descartes.patch import PolygonPatch
|
|
|
@@ -25,14 +17,21 @@ from descartes.patch import PolygonPatch
|
|
|
from shapely.geometry import Polygon, LineString, LinearRing, Point, MultiPolygon, MultiLineString
|
|
|
|
|
|
import FlatCAMApp
|
|
|
+
|
|
|
from copy import deepcopy
|
|
|
import logging
|
|
|
-import traceback
|
|
|
|
|
|
import gettext
|
|
|
import FlatCAMTranslation as fcTranslate
|
|
|
import builtins
|
|
|
|
|
|
+# Prevent conflict with Qt5 and above.
|
|
|
+from matplotlib import use as mpl_use
|
|
|
+mpl_use("Qt5Agg")
|
|
|
+from matplotlib.figure import Figure
|
|
|
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
|
+# from matplotlib.widgets import Cursor
|
|
|
+
|
|
|
fcTranslate.apply_language('strings')
|
|
|
if '_' not in builtins.__dict__:
|
|
|
_ = gettext.gettext
|
|
|
@@ -78,7 +77,7 @@ class CanvasCache(QtCore.QObject):
|
|
|
self.axes.set_xticks([])
|
|
|
self.axes.set_yticks([])
|
|
|
|
|
|
- self.canvas = FigureCanvasAgg(self.figure)
|
|
|
+ self.canvas = FigureCanvas(self.figure)
|
|
|
|
|
|
self.cache = None
|
|
|
|
|
|
@@ -115,33 +114,6 @@ class CanvasCache(QtCore.QObject):
|
|
|
# log.debug("A new object is available. Should plot it!")
|
|
|
|
|
|
|
|
|
-class FigureCanvas(FigureCanvasQTAgg):
|
|
|
- """
|
|
|
- Reimplemented this so I can emit a signal when the idle drawing is finished and display the mouse shape
|
|
|
- """
|
|
|
-
|
|
|
- idle_drawing_finished = pyqtSignal()
|
|
|
-
|
|
|
- def __init__(self, figure):
|
|
|
- super().__init__(figure=figure)
|
|
|
-
|
|
|
- def _draw_idle(self):
|
|
|
- if self.height() < 0 or self.width() < 0:
|
|
|
- self._draw_pending = False
|
|
|
- if not self._draw_pending:
|
|
|
- return
|
|
|
- try:
|
|
|
- self.draw()
|
|
|
- except Exception:
|
|
|
- # Uncaught exceptions are fatal for PyQt5, so catch them instead.
|
|
|
- traceback.print_exc()
|
|
|
- finally:
|
|
|
- self._draw_pending = False
|
|
|
-
|
|
|
- # I reimplemented this class only to launch this signal
|
|
|
- self.idle_drawing_finished.emit()
|
|
|
-
|
|
|
-
|
|
|
class PlotCanvasLegacy(QtCore.QObject):
|
|
|
"""
|
|
|
Class handling the plotting area in the application.
|
|
|
@@ -151,6 +123,7 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
# 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):
|
|
|
@@ -188,6 +161,7 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
|
|
|
# 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
|
|
|
@@ -203,15 +177,17 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
# Update every time the canvas is re-drawn.
|
|
|
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
|
|
|
|
|
|
+ # ################### NOT IMPLEMENTED YET - EXPERIMENTAL #######################
|
|
|
# ## 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)
|
|
|
+ # 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.mp = self.graph_event_connect('button_press_event', self.on_mouse_press)
|
|
|
@@ -226,11 +202,11 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
# self.graph_event_connect('key_release_event', self.on_key_up)
|
|
|
self.odr = self.graph_event_connect('draw_event', self.on_draw)
|
|
|
|
|
|
- self.mouse = [0, 0]
|
|
|
self.key = None
|
|
|
|
|
|
self.pan_axes = []
|
|
|
self.panning = False
|
|
|
+ self.mouse = [0, 0]
|
|
|
|
|
|
# signal is the mouse is dragging
|
|
|
self.is_dragging = False
|
|
|
@@ -238,9 +214,6 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
# signal if there is a doubleclick
|
|
|
self.is_dblclk = False
|
|
|
|
|
|
- # pay attention, this signal should be connected only after the self.canvas and self.mouse is declared
|
|
|
- self.canvas.idle_drawing_finished.connect(lambda: self.draw_cursor(x_pos=self.mouse[0], y_pos=self.mouse[1]))
|
|
|
-
|
|
|
def graph_event_connect(self, event_name, callback):
|
|
|
"""
|
|
|
Attach an event handler to the canvas through the Matplotlib interface.
|
|
|
@@ -294,6 +267,31 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
print(str(e))
|
|
|
return c
|
|
|
|
|
|
+ def draw_cursor(self, x_pos, y_pos):
|
|
|
+ """
|
|
|
+ Draw a cursor at the mouse grid snapped position
|
|
|
+
|
|
|
+ :param x_pos: mouse x position
|
|
|
+ :param y_pos: mouse y position
|
|
|
+ :return:
|
|
|
+ """
|
|
|
+ # 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)
|
|
|
+
|
|
|
+ # Pointer (snapped)
|
|
|
+ elements = self.axes.plot(x, y, 'k+', ms=40, mew=2, 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
|
|
|
+
|
|
|
+ self.canvas.blit(self.axes.bbox)
|
|
|
+
|
|
|
def clear_cursor(self, state):
|
|
|
|
|
|
if state is True:
|
|
|
@@ -653,31 +651,6 @@ class PlotCanvasLegacy(QtCore.QObject):
|
|
|
|
|
|
# self.canvas.blit(self.axes.bbox)
|
|
|
|
|
|
- def draw_cursor(self, x_pos, y_pos):
|
|
|
- """
|
|
|
- Draw a cursor at the mouse grid snapped position
|
|
|
-
|
|
|
- :param x_pos: mouse x position
|
|
|
- :param y_pos: mouse y position
|
|
|
- :return:
|
|
|
- """
|
|
|
- # 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)
|
|
|
-
|
|
|
- # Pointer (snapped)
|
|
|
- elements = self.axes.plot(x, y, 'k+', ms=40, mew=2, 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
|
|
|
-
|
|
|
- self.canvas.blit(self.axes.bbox)
|
|
|
-
|
|
|
def translate_coords(self, position):
|
|
|
"""
|
|
|
This does not do much. It's just for code compatibility
|
|
|
@@ -750,66 +723,6 @@ class FakeCursor(QtCore.QObject):
|
|
|
pass
|
|
|
|
|
|
|
|
|
-class MplCursor(Cursor):
|
|
|
- """
|
|
|
- Unfortunately this gets attached to the current axes and if a new axes is added
|
|
|
- it will not be showed until that axes is deleted.
|
|
|
- Not the kind of behavior needed here so I don't use it anymore.
|
|
|
- """
|
|
|
- def __init__(self, axes, color='red', linewidth=1):
|
|
|
-
|
|
|
- super().__init__(ax=axes, useblit=True, color=color, linewidth=linewidth)
|
|
|
- self._enabled = True
|
|
|
-
|
|
|
- self.axes = axes
|
|
|
- self.color = color
|
|
|
- self.linewidth = linewidth
|
|
|
-
|
|
|
- self.x = None
|
|
|
- self.y = None
|
|
|
-
|
|
|
- @property
|
|
|
- def enabled(self):
|
|
|
- return True if self._enabled else False
|
|
|
-
|
|
|
- @enabled.setter
|
|
|
- def enabled(self, value):
|
|
|
- self._enabled = value
|
|
|
- self.visible = self._enabled
|
|
|
- self.canvas.draw()
|
|
|
-
|
|
|
- def onmove(self, event):
|
|
|
- pass
|
|
|
-
|
|
|
- def set_data(self, event, pos):
|
|
|
- """Internal event handler to draw the cursor when the mouse moves."""
|
|
|
- self.x = pos[0]
|
|
|
- self.y = pos[1]
|
|
|
-
|
|
|
- if self.ignore(event):
|
|
|
- return
|
|
|
- if not self.canvas.widgetlock.available(self):
|
|
|
- return
|
|
|
- if event.inaxes != self.ax:
|
|
|
- self.linev.set_visible(False)
|
|
|
- self.lineh.set_visible(False)
|
|
|
-
|
|
|
- if self.needclear:
|
|
|
- self.canvas.draw()
|
|
|
- self.needclear = False
|
|
|
- return
|
|
|
- self.needclear = True
|
|
|
- if not self.visible:
|
|
|
- return
|
|
|
- self.linev.set_xdata((self.x, self.x))
|
|
|
-
|
|
|
- self.lineh.set_ydata((self.y, self.y))
|
|
|
- self.linev.set_visible(self.visible and self.vertOn)
|
|
|
- self.lineh.set_visible(self.visible and self.horizOn)
|
|
|
-
|
|
|
- self._update()
|
|
|
-
|
|
|
-
|
|
|
class ShapeCollectionLegacy:
|
|
|
"""
|
|
|
This will create the axes for each collection of shapes and will also
|
|
|
@@ -930,7 +843,10 @@ class ShapeCollectionLegacy:
|
|
|
self.shape_id = 0
|
|
|
|
|
|
self.axes.cla()
|
|
|
- self.app.plotcanvas.auto_adjust_axes()
|
|
|
+ try:
|
|
|
+ self.app.plotcanvas.auto_adjust_axes()
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("ShapeCollectionLegacy.clear() --> %s" % str(e))
|
|
|
|
|
|
if update is True:
|
|
|
self.redraw()
|
|
|
@@ -1013,12 +929,17 @@ class ShapeCollectionLegacy:
|
|
|
self.axes.plot(x, y, linespec, color=linecolor)
|
|
|
else:
|
|
|
path_num += 1
|
|
|
- if isinstance(local_shapes[element]['shape'], Polygon):
|
|
|
- self.axes.annotate(str(path_num), xy=local_shapes[element]['shape'].exterior.coords[0],
|
|
|
- xycoords='data', fontsize=20)
|
|
|
- else:
|
|
|
- self.axes.annotate(str(path_num), xy=local_shapes[element]['shape'].coords[0],
|
|
|
- xycoords='data', fontsize=20)
|
|
|
+ if self.obj.ui.annotation_cb.get_value():
|
|
|
+ if isinstance(local_shapes[element]['shape'], Polygon):
|
|
|
+ self.axes.annotate(
|
|
|
+ str(path_num),
|
|
|
+ xy=local_shapes[element]['shape'].exterior.coords[0],
|
|
|
+ xycoords='data', fontsize=20)
|
|
|
+ else:
|
|
|
+ self.axes.annotate(
|
|
|
+ str(path_num),
|
|
|
+ xy=local_shapes[element]['shape'].coords[0],
|
|
|
+ xycoords='data', fontsize=20)
|
|
|
|
|
|
patch = PolygonPatch(local_shapes[element]['shape'],
|
|
|
facecolor=local_shapes[element]['face_color'],
|
|
|
@@ -1108,3 +1029,62 @@ class ShapeCollectionLegacy:
|
|
|
if self._visible is False:
|
|
|
self.redraw()
|
|
|
self._visible = value
|
|
|
+
|
|
|
+# class MplCursor(Cursor):
|
|
|
+# """
|
|
|
+# Unfortunately this gets attached to the current axes and if a new axes is added
|
|
|
+# it will not be showed until that axes is deleted.
|
|
|
+# Not the kind of behavior needed here so I don't use it anymore.
|
|
|
+# """
|
|
|
+# def __init__(self, axes, color='red', linewidth=1):
|
|
|
+#
|
|
|
+# super().__init__(ax=axes, useblit=True, color=color, linewidth=linewidth)
|
|
|
+# self._enabled = True
|
|
|
+#
|
|
|
+# self.axes = axes
|
|
|
+# self.color = color
|
|
|
+# self.linewidth = linewidth
|
|
|
+#
|
|
|
+# self.x = None
|
|
|
+# self.y = None
|
|
|
+#
|
|
|
+# @property
|
|
|
+# def enabled(self):
|
|
|
+# return True if self._enabled else False
|
|
|
+#
|
|
|
+# @enabled.setter
|
|
|
+# def enabled(self, value):
|
|
|
+# self._enabled = value
|
|
|
+# self.visible = self._enabled
|
|
|
+# self.canvas.draw()
|
|
|
+#
|
|
|
+# def onmove(self, event):
|
|
|
+# pass
|
|
|
+#
|
|
|
+# def set_data(self, event, pos):
|
|
|
+# """Internal event handler to draw the cursor when the mouse moves."""
|
|
|
+# self.x = pos[0]
|
|
|
+# self.y = pos[1]
|
|
|
+#
|
|
|
+# if self.ignore(event):
|
|
|
+# return
|
|
|
+# if not self.canvas.widgetlock.available(self):
|
|
|
+# return
|
|
|
+# if event.inaxes != self.ax:
|
|
|
+# self.linev.set_visible(False)
|
|
|
+# self.lineh.set_visible(False)
|
|
|
+#
|
|
|
+# if self.needclear:
|
|
|
+# self.canvas.draw()
|
|
|
+# self.needclear = False
|
|
|
+# return
|
|
|
+# self.needclear = True
|
|
|
+# if not self.visible:
|
|
|
+# return
|
|
|
+# self.linev.set_xdata((self.x, self.x))
|
|
|
+#
|
|
|
+# self.lineh.set_ydata((self.y, self.y))
|
|
|
+# self.linev.set_visible(self.visible and self.vertOn)
|
|
|
+# self.lineh.set_visible(self.visible and self.horizOn)
|
|
|
+#
|
|
|
+# self._update()
|