Repository: vispy/vispy

Issue:
API Specification: event system

examples/basics/visuals/line_draw.py
Hi,
I can't run the example line_draw.py. Someone can try if it is only my problem or if there is an issue?

```

Traceback (most recent call last):
  File "/home/........../basics/visuals/line_draw.py", line 178, in <module>
    win = Canvas()
  File "/home/........../basics/visuals/line_draw.py", line 154, in __init__
    self.pos = np.zeros((n, 3), dtype=np.float32)
  File "/home/........../lib/python3.6/site-packages/vispy/util/frozen.py", line 16, in __setattr__
    'attributes' % (key, self))
AttributeError: 'pos' is not an attribute of class <Canvas (PyQt5) at 0x7f4334088f98>. Call "unfreeze()" to allow addition of new attributes

Process finished with exit code 1

```


Discussion:
We are tweaking some details (like scrolling), but I think we can close this one, right?

Fine by me!



Resolving patch:
diff --git a/examples/basics/visuals/line_draw.py b/examples/basics/visuals/line_draw.py
--- a/examples/basics/visuals/line_draw.py
+++ b/examples/basics/visuals/line_draw.py
@@ -26,9 +26,9 @@ class EditLineVisual(scene.visuals.Line):
 
     def __init__(self, *args, **kwargs):
         scene.visuals.Line.__init__(self, *args, **kwargs)
-
+        self.unfreeze()
         # initialize point markers
-        self.markers = scene.visuals.Markers()
+        self.markers = scene.visuals.Markers(parent=self)
         self.marker_colors = np.ones((len(self.pos), 4), dtype=np.float32)
         self.markers.set_data(pos=self.pos, symbol="s", edge_color="red",
                               size=6)
@@ -36,18 +36,19 @@ def __init__(self, *args, **kwargs):
         self.selected_index = -1
         # snap grid size
         self.gridsize = 10
+        self.freeze()
 
-    def draw(self, transforms):
+    def on_draw(self, event):
         # draw line and markers
-        scene.visuals.Line.draw(self, transforms)
-        self.markers.draw(transforms)
+        scene.visuals.Line.draw(self)
+        self.markers.draw()
 
     def print_mouse_event(self, event, what):
         """ print mouse events for debugging purposes """
         print('%s - pos: %r, button: %s,  delta: %r' %
               (what, event.pos, event.button, event.delta))
 
-    def select_point(self, event, radius=5):
+    def select_point(self, pos_scene, radius=5):
 
         """
         Get line point close to mouse pointer and its index
@@ -61,19 +62,15 @@ def select_point(self, event, radius=5):
             picked point and index of the point in the pos array
         """
 
-        # position in scene/document coordinates
-        pos_scene = event.pos[:3]
-
         # project mouse radius from screen coordinates to document coordinates
-        mouse_radius = \
-            (event.visual_to_canvas.imap(np.array([radius, radius, radius])) -
-             event.visual_to_canvas.imap(np.array([0, 0, 0])))[0]
+
+        mouse_radius = 6
         # print("Mouse radius in document units: ", mouse_radius)
 
         # find first point within mouse_radius
         index = 0
         for p in self.pos:
-            if np.linalg.norm(pos_scene - p) < mouse_radius:
+            if np.linalg.norm(pos_scene[:3] - p) < mouse_radius:
                 # print p, index
                 # point found, return point and its index
                 return p, index
@@ -96,17 +93,17 @@ def update_markers(self, selected_index=-1, highlight_color=(1, 0, 0, 1)):
         self.markers.set_data(pos=self.pos, symbol=shape, edge_color='red',
                               size=size, face_color=self.marker_colors)
 
-    def on_mouse_press(self, event):
-        self.print_mouse_event(event, 'Mouse press')
-        pos_scene = event.pos[:3]
+    def on_mouse_press(self, pos_scene):
+        # self.print_mouse_event(event, 'Mouse press')
+        # pos_scene = event.pos[:3]
 
         # find closest point to mouse and select it
-        self.selected_point, self.selected_index = self.select_point(event)
+        self.selected_point, self.selected_index = self.select_point(pos_scene)
 
         # if no point was clicked add a new one
         if self.selected_point is None:
             print("adding point", len(self.pos))
-            self._pos = np.append(self.pos, [pos_scene], axis=0)
+            self._pos = np.append(self.pos, [pos_scene[:3]], axis=0)
             self.set_data(pos=self.pos)
             self.marker_colors = np.ones((len(self.pos), 4), dtype=np.float32)
             self.selected_point = self.pos[-1]
@@ -120,26 +117,22 @@ def on_mouse_release(self, event):
         self.selected_point = None
         self.update_markers()
 
-    def on_mouse_move(self, event):
-        # left mouse button
-        if event.button == 1:
-            # self.print_mouse_event(event, 'Mouse drag')
-            if self.selected_point is not None:
-                pos_scene = event.pos
-                # update selected point to new position given by mouse
-                self.selected_point[0] = round(pos_scene[0] / self.gridsize) \
-                    * self.gridsize
-                self.selected_point[1] = round(pos_scene[1] / self.gridsize) \
-                    * self.gridsize
-                self.set_data(pos=self.pos)
-                self.update_markers(self.selected_index)
+    def on_mouse_move(self, pos_scene):
+        if self.selected_point is not None:
+            # update selected point to new position given by mouse
+            self.selected_point[0] = round(pos_scene[0] / self.gridsize) \
+                * self.gridsize
+            self.selected_point[1] = round(pos_scene[1] / self.gridsize) \
+                * self.gridsize
+            self.set_data(pos=self.pos)
+            self.update_markers(self.selected_index)
 
-        else:
-            #  if no button is pressed, just highlight the marker that would be
-            # selected on click
-            hl_point, hl_index = self.select_point(event)
-            self.update_markers(hl_index, highlight_color=(0.5, 0.5, 1.0, 1.0))
-            self.update()
+    def highlight_markers(self, pos_scene):
+        #  if no button is pressed, just highlight the marker that would be
+        # selected on click
+        hl_point, hl_index = self.select_point(pos_scene)
+        self.update_markers(hl_index, highlight_color=(0.5, 0.5, 1.0, 1.0))
+        self.update()
 
 
 class Canvas(scene.SceneCanvas):
@@ -151,6 +144,7 @@ def __init__(self):
 
         # Create some initial points
         n = 7
+        self.unfreeze()
         self.pos = np.zeros((n, 3), dtype=np.float32)
         self.pos[:, 0] = np.linspace(-50, 50, n)
         self.pos[:, 1] = np.random.normal(size=n, scale=10, loc=0)
@@ -172,6 +166,21 @@ def __init__(self):
         self.show()
         self.selected_point = None
         scene.visuals.GridLines(parent=self.view.scene)
+        self.freeze()
+
+    def on_mouse_press(self, event):
+        tr = self.scene.node_transform(self.line)
+        pos = tr.map(event.pos)
+        self.line.on_mouse_press(pos)
+
+    def on_mouse_move(self, event):
+        # left mouse button
+        tr = self.scene.node_transform(self.line)
+        pos = tr.map(event.pos)
+        if event.button == 1:
+            self.line.on_mouse_move(pos)
+        else:
+            self.line.highlight_markers(pos)
 
 
 if __name__ == '__main__':


Test patch: