Repository: vispy/vispy

Issue:
Bug with console example
occurs when clicking in the window (tested on Windows)

```
WARNING: Traceback (most recent call last):
  File "examples/basics/scene/console.py", line 46, in <module>
    canvas.app.run()
  File "D:\Git\vispy\vispy\app\application.py", line 88, in run
    return self._backend._vispy_run()
  File "D:\Git\vispy\vispy\app\backends\_qt.py", line 175, in _vispy_run
    return app.exec_()
  File "D:\Git\vispy\vispy\app\backends\_qt.py", line 370, in mouseMoveEvent
    modifiers=self._modifiers(ev),
  File "D:\Git\vispy\vispy\app\base.py", line 201, in _vispy_mouse_move
    ev = self._vispy_canvas.events.mouse_move(**kwds)
  File "D:\Git\vispy\vispy\util\event.py", line 418, in __call__
    self._invoke_callback(cb, event)
  File "D:\Git\vispy\vispy\util\event.py", line 446, in _invoke_callback
    self, cb_event=(cb, event))
  << caught exception here: >>
  File "D:\Git\vispy\vispy\util\event.py", line 435, in _invoke_callback
    cb(event)
  File "D:\Git\vispy\vispy\scene\canvas.py", line 194, in _process_mouse_event
    self._scene._process_mouse_event(scene_event)
  File "D:\Git\vispy\vispy\scene\subscene.py", line 44, in _process_mouse_event
    self.process_system(event, 'mouse')
  File "D:\Git\vispy\vispy\scene\subscene.py", line 49, in process_system
    self._systems[system_name].process(event, self)
  File "D:\Git\vispy\vispy\scene\systems.py", line 63, in process
    self._process_entity(event, subscene)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 76, in _process_entity
    deliver = entity.rect.contains(*event.press_event.pos[:2])
  File "D:\Git\vispy\vispy\scene\events.py", line 332, in pos
    return self.map_from_canvas(self.mouse_event.pos)
  File "D:\Git\vispy\vispy\scene\events.py", line 191, in map_from_canvas
    return self.canvas_transform().imap(obj)
  File "D:\Git\vispy\vispy\scene\events.py", line 179, in canvas_transform
    return self.entity_transform(map_to=self.canvas_cs, map_from=entity)
  File "D:\Git\vispy\vispy\scene\events.py", line 273, in entity_transform
    (map_from, map_to))
RuntimeError: Unable to find unique path from <ViewBox at 0x753ad30> to <Entity
at 0x7536c50>
WARNING: Error invoking callback <bound method SceneCanvas._process_mouse_event
of <Vispy canvas (PyQt4 (qt) backend) at 0x3aa4080L>> for event: <MouseEvent blo
cked=False button=1 buttons=[1] delta=(0.0, 0.0) handled=False is_dragging=True
last_event=<MouseEvent blocked=False button=1 buttons=[1] delta=(0.0, 0.0) handl
ed=False is_dragging=False last_event=<...> modifiers=() native=<PyQt4.QtGui.QMo
useEvent object at 0x00000000077149D8> pos=(258, 84) press_event=None source=Non
e sources=[] type=mouse_press> modifiers=() native=<PyQt4.QtGui.QMouseEvent obje
ct at 0x00000000077149D8> pos=(258, 85) press_event=<MouseEvent blocked=False bu
tton=1 buttons=[1] delta=(0.0, 0.0) handled=False is_dragging=False last_event=<
...> modifiers=() native=<PyQt4.QtGui.QMouseEvent object at 0x00000000077149D8>
pos=(258, 84) press_event=None source=None sources=[] type=mouse_press> source=<
Vispy canvas (PyQt4 (qt) backend) at 0x3aa4080L> sources=[<Vispy canvas (PyQt4 (
qt) backend) at 0x3aa4080L>] type=mouse_move>
WARNING: Traceback (most recent call last):
  File "examples/basics/scene/console.py", line 46, in <module>
    canvas.app.run()
  File "D:\Git\vispy\vispy\app\application.py", line 88, in run
    return self._backend._vispy_run()
  File "D:\Git\vispy\vispy\app\backends\_qt.py", line 175, in _vispy_run
    return app.exec_()
  File "D:\Git\vispy\vispy\app\backends\_qt.py", line 361, in mouseReleaseEvent
    modifiers = self._modifiers(ev),
  File "D:\Git\vispy\vispy\app\base.py", line 208, in _vispy_mouse_release
    ev = self._vispy_canvas.events.mouse_release(**kwds)
  File "D:\Git\vispy\vispy\util\event.py", line 418, in __call__
    self._invoke_callback(cb, event)
  File "D:\Git\vispy\vispy\util\event.py", line 446, in _invoke_callback
    self, cb_event=(cb, event))
  << caught exception here: >>
  File "D:\Git\vispy\vispy\util\event.py", line 435, in _invoke_callback
    cb(event)
  File "D:\Git\vispy\vispy\scene\canvas.py", line 194, in _process_mouse_event
    self._scene._process_mouse_event(scene_event)
  File "D:\Git\vispy\vispy\scene\subscene.py", line 44, in _process_mouse_event
    self.process_system(event, 'mouse')
  File "D:\Git\vispy\vispy\scene\subscene.py", line 49, in process_system
    self._systems[system_name].process(event, self)
  File "D:\Git\vispy\vispy\scene\systems.py", line 63, in process
    self._process_entity(event, subscene)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 84, in _process_entity
    self._process_entity(event, sub_entity)
  File "D:\Git\vispy\vispy\scene\systems.py", line 76, in _process_entity
    deliver = entity.rect.contains(*event.press_event.pos[:2])
  File "D:\Git\vispy\vispy\scene\events.py", line 332, in pos
    return self.map_from_canvas(self.mouse_event.pos)
  File "D:\Git\vispy\vispy\scene\events.py", line 191, in map_from_canvas
    return self.canvas_transform().imap(obj)
  File "D:\Git\vispy\vispy\scene\events.py", line 179, in canvas_transform
    return self.entity_transform(map_to=self.canvas_cs, map_from=entity)
  File "D:\Git\vispy\vispy\scene\events.py", line 273, in entity_transform
    (map_from, map_to))
RuntimeError: Unable to find unique path from <ViewBox at 0x753ad30> to <Entity
at 0x7536c50>
WARNING: Error invoking callback <bound method SceneCanvas._process_mouse_event
of <Vispy canvas (PyQt4 (qt) backend) at 0x3aa4080L>> for event: <MouseEvent blo
cked=False button=1 buttons=[1] delta=(0.0, 0.0) handled=False is_dragging=True
last_event=<MouseEvent blocked=False button=1 buttons=[1] delta=(0.0, 0.0) handl
ed=False is_dragging=True last_event=<...> modifiers=() native=<PyQt4.QtGui.QMou
seEvent object at 0x00000000077149D8> pos=(258, 85) press_event=<...> source=Non
e sources=[] type=mouse_move> modifiers=() native=<PyQt4.QtGui.QMouseEvent objec
t at 0x00000000077149D8> pos=(258, 85) press_event=<MouseEvent blocked=False but
ton=1 buttons=[1] delta=(0.0, 0.0) handled=False is_dragging=False last_event=<.
..> modifiers=() native=<PyQt4.QtGui.QMouseEvent object at 0x00000000077149D8> p
os=(258, 84) press_event=None source=None sources=[] type=mouse_press> source=<V
ispy canvas (PyQt4 (qt) backend) at 0x3aa4080L> sources=[<Vispy canvas (PyQt4 (q
t) backend) at 0x3aa4080L>] type=mouse_release>
```



Discussion:
Yep, this is a problem. @lcampagn I think it just has to do with the fact that it's a `Widget` (i.e., not specific to the console widget) -- any idea what this is about?


Resolving patch:
diff --git a/examples/basics/scene/console.py b/examples/basics/scene/console.py
--- a/examples/basics/scene/console.py
+++ b/examples/basics/scene/console.py
@@ -15,14 +15,14 @@
 from vispy.scene.visuals import Text
 
 canvas = scene.SceneCanvas(keys='interactive', size=(400, 400))
-vb = scene.widgets.ViewBox(parent=canvas.scene, border_color='b')
+grid = canvas.central_widget.add_grid()
+
+vb = scene.widgets.ViewBox(border_color='b')
 vb.camera.rect = -1, -1, 2, 2
+grid.add_widget(vb, row=0, col=0)
 text = Text('Starting timer...', color='w', font_size=24, parent=vb.scene)
 
 console = Console(text_color='g', font_size=12., border_color='g')
-
-grid = canvas.central_widget.add_grid()
-grid.add_widget(vb, row=0, col=0)
 grid.add_widget(console, row=1, col=0)
 
 
diff --git a/vispy/scene/canvas.py b/vispy/scene/canvas.py
--- a/vispy/scene/canvas.py
+++ b/vispy/scene/canvas.py
@@ -99,10 +99,12 @@ def __init__(self, *args, **kwargs):
         self._transform_caches = weakref.WeakKeyDictionary()
 
         # Set up default entity stack: ndc -> fb -> canvas -> scene
-        self.render_cs = Entity()
-        self.framebuffer_cs = Entity(parent=self.render_cs)
+        self.render_cs = Entity(name="render_cs")
+        self.framebuffer_cs = Entity(parent=self.render_cs, 
+                                     name="framebuffer_cs")
         self.framebuffer_cs.transform = STTransform()
-        self.canvas_cs = Entity(parent=self.framebuffer_cs)
+        self.canvas_cs = Entity(parent=self.framebuffer_cs,
+                                name="canvas_cs")
         self.canvas_cs.transform = STTransform()
         # By default, the document coordinate system is the canvas.
         self.canvas_cs.document = self.canvas_cs
diff --git a/vispy/scene/widgets/grid.py b/vispy/scene/widgets/grid.py
--- a/vispy/scene/widgets/grid.py
+++ b/vispy/scene/widgets/grid.py
@@ -24,7 +24,27 @@ def __init__(self, **kwds):
     def add_widget(self, widget=None, row=None, col=None, row_span=1, 
                    col_span=1):
         """
-        Add a new widget to this grid.
+        Add a new widget to this grid. This will cause other widgets in the
+        grid to be resized to make room for the new widget.
+        
+        Parameters
+        ----------
+        widget : Widget
+            The Widget to add
+        row : int
+            The row in which to add the widget (0 is the topmost row)
+        col : int
+            The column in which to add the widget (0 is the leftmost column)
+        row_span : int
+            The number of rows to be occupied by this widget. Default is 1.
+        col_span : int
+            The number of columns to be occupied by this widget. Default is 1.
+        
+        Notes
+        -----
+        
+        The widget's parent is automatically set to this grid, and all other 
+        parent(s) are removed.
         """
         if row is None:
             row = self._next_cell[0]
@@ -37,7 +57,7 @@ def add_widget(self, widget=None, row=None, col=None, row_span=1,
         _row = self._cells.setdefault(row, {})
         _row[col] = widget
         self._grid_widgets[widget] = row, col, row_span, col_span
-        widget.add_parent(self)
+        widget.parent = self
 
         self._next_cell = [row, col+col_span]
         self._update_child_widgets()


Test patch: