PlotCanvas.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. ############################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://caram.cl/software/flatcam #
  4. # Author: Juan Pablo Caram (c) #
  5. # Date: 2/5/2014 #
  6. # MIT Licence #
  7. ############################################################
  8. from PyQt5 import QtCore
  9. import logging
  10. from VisPyCanvas import VisPyCanvas
  11. from VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
  12. from vispy.scene.visuals import InfiniteLine, Line
  13. import numpy as np
  14. from vispy.geometry import Rect
  15. import time
  16. log = logging.getLogger('base')
  17. class PlotCanvas(QtCore.QObject):
  18. """
  19. Class handling the plotting area in the application.
  20. """
  21. def __init__(self, container, app):
  22. """
  23. The constructor configures the VisPy figure that
  24. will contain all plots, creates the base axes and connects
  25. events to the plotting area.
  26. :param container: The parent container in which to draw plots.
  27. :rtype: PlotCanvas
  28. """
  29. super(PlotCanvas, self).__init__()
  30. self.app = app
  31. # Parent container
  32. self.container = container
  33. # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
  34. # which might decrease performance
  35. self.b_line, self.r_line, self.t_line, self.l_line = None, None, None, None
  36. # Attach to parent
  37. self.vispy_canvas = VisPyCanvas()
  38. self.vispy_canvas.create_native()
  39. self.vispy_canvas.native.setParent(self.app.ui)
  40. self.container.addWidget(self.vispy_canvas.native)
  41. ### AXIS ###
  42. self.v_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=True,
  43. parent=self.vispy_canvas.view.scene)
  44. self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=False,
  45. parent=self.vispy_canvas.view.scene)
  46. # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
  47. # all CNC have a limited workspace
  48. self.draw_workspace()
  49. # if self.app.defaults['global_workspace'] is True:
  50. # if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
  51. # self.wkspace_t = Line(pos=)
  52. self.shape_collections = []
  53. self.shape_collection = self.new_shape_collection()
  54. self.app.pool_recreated.connect(self.on_pool_recreated)
  55. self.text_collection = self.new_text_collection()
  56. # TODO: Should be setting to show/hide CNC job annotations (global or per object)
  57. self.text_collection.enabled = False
  58. # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
  59. # all CNC have a limited workspace
  60. def draw_workspace(self):
  61. a = np.empty((0, 0))
  62. a4p_in = np.array([(0, 0), (8.3, 0), (8.3, 11.7), (0, 11.7)])
  63. a4l_in = np.array([(0, 0), (11.7, 0), (11.7, 8.3), (0, 8.3)])
  64. a3p_in = np.array([(0, 0), (11.7, 0), (11.7, 16.5), (0, 16.5)])
  65. a3l_in = np.array([(0, 0), (16.5, 0), (16.5, 11.7), (0, 11.7)])
  66. a4p_mm = np.array([(0, 0), (210, 0), (210, 297), (0, 297)])
  67. a4l_mm = np.array([(0, 0), (297, 0), (297,210), (0, 210)])
  68. a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
  69. a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
  70. if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
  71. if self.app.defaults['global_workspaceT'] == 'A4P':
  72. a = a4p_mm
  73. elif self.app.defaults['global_workspaceT'] == 'A4L':
  74. a = a4l_mm
  75. elif self.app.defaults['global_workspaceT'] == 'A3P':
  76. a = a3p_mm
  77. elif self.app.defaults['global_workspaceT'] == 'A3L':
  78. a = a3l_mm
  79. else:
  80. if self.app.defaults['global_workspaceT'] == 'A4P':
  81. a = a4p_in
  82. elif self.app.defaults['global_workspaceT'] == 'A4L':
  83. a = a4l_in
  84. elif self.app.defaults['global_workspaceT'] == 'A3P':
  85. a = a3p_in
  86. elif self.app.defaults['global_workspaceT'] == 'A3L':
  87. a = a3l_in
  88. self.delete_workspace()
  89. self.b_line = Line(pos=a[0:2], color=(0.70, 0.3, 0.3, 1.0),
  90. antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
  91. self.r_line = Line(pos=a[1:3], color=(0.70, 0.3, 0.3, 1.0),
  92. antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
  93. self.t_line = Line(pos=a[2:4], color=(0.70, 0.3, 0.3, 1.0),
  94. antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
  95. self.l_line = Line(pos=np.array((a[0], a[3])), color=(0.70, 0.3, 0.3, 1.0),
  96. antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
  97. if self.app.defaults['global_workspace'] is False:
  98. self.delete_workspace()
  99. # delete the workspace lines from the plot by removing the parent
  100. def delete_workspace(self):
  101. try:
  102. self.b_line.parent = None
  103. self.r_line.parent = None
  104. self.t_line.parent = None
  105. self.l_line.parent = None
  106. except:
  107. pass
  108. # redraw the workspace lines on the plot by readding them to the parent view.scene
  109. def restore_workspace(self):
  110. try:
  111. self.b_line.parent = self.vispy_canvas.view.scene
  112. self.r_line.parent = self.vispy_canvas.view.scene
  113. self.t_line.parent = self.vispy_canvas.view.scene
  114. self.l_line.parent = self.vispy_canvas.view.scene
  115. except:
  116. pass
  117. def vis_connect(self, event_name, callback):
  118. return getattr(self.vispy_canvas.events, event_name).connect(callback)
  119. def vis_disconnect(self, event_name, callback):
  120. getattr(self.vispy_canvas.events, event_name).disconnect(callback)
  121. def zoom(self, factor, center=None):
  122. """
  123. Zooms the plot by factor around a given
  124. center point. Takes care of re-drawing.
  125. :param factor: Number by which to scale the plot.
  126. :type factor: float
  127. :param center: Coordinates [x, y] of the point around which to scale the plot.
  128. :type center: list
  129. :return: None
  130. """
  131. self.vispy_canvas.view.camera.zoom(factor, center)
  132. def new_shape_group(self):
  133. return ShapeGroup(self.shape_collection)
  134. def new_shape_collection(self, **kwargs):
  135. # sc = ShapeCollection(parent=self.vispy_canvas.view.scene, pool=self.app.pool, **kwargs)
  136. # self.shape_collections.append(sc)
  137. # return sc
  138. return ShapeCollection(parent=self.vispy_canvas.view.scene, pool=self.app.pool, **kwargs)
  139. def new_cursor(self):
  140. c = Cursor(pos=np.empty((0, 2)), parent=self.vispy_canvas.view.scene)
  141. c.antialias = 0
  142. return c
  143. def new_text_group(self):
  144. return TextGroup(self.text_collection)
  145. def new_text_collection(self, **kwargs):
  146. return TextCollection(parent=self.vispy_canvas.view.scene, **kwargs)
  147. def fit_view(self, rect=None):
  148. # Lock updates in other threads
  149. self.shape_collection.lock_updates()
  150. if not rect:
  151. rect = Rect(-1, -1, 20, 20)
  152. try:
  153. rect.left, rect.right = self.shape_collection.bounds(axis=0)
  154. rect.bottom, rect.top = self.shape_collection.bounds(axis=1)
  155. except TypeError:
  156. pass
  157. self.vispy_canvas.view.camera.rect = rect
  158. self.shape_collection.unlock_updates()
  159. def fit_center(self, loc, rect=None):
  160. # Lock updates in other threads
  161. self.shape_collection.lock_updates()
  162. if not rect:
  163. try:
  164. rect = Rect(loc[0]-20, loc[1]-20, 40, 40)
  165. except TypeError:
  166. pass
  167. self.vispy_canvas.view.camera.rect = rect
  168. self.shape_collection.unlock_updates()
  169. def clear(self):
  170. pass
  171. def redraw(self):
  172. self.shape_collection.redraw([])
  173. self.text_collection.redraw()
  174. def on_pool_recreated(self, pool):
  175. self.shape_collection.pool = pool