We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Update volume_plane.py to allow timer to be stopped
I noticed the volume_plane.py has keyboard handling for moving the plane through the volume, but it doesn't allow you to stop the animation so these events don't actually mean much.

@alisterburt do you have time to fix this? Tradition is to use the spacebar (`" "`) to toggle the timer start/stop

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/scene/volume_plane.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # -----------------------------------------------------------------------------
3 # Copyright (c) Vispy Development Team. All Rights Reserved.
4 # Distributed under the (new) BSD License. See LICENSE.txt for more info.
5 # -----------------------------------------------------------------------------
6 # vispy: gallery 10:200:5
7 """
8 Rendering Planes through 3D Data
9 ================================
10 
11 Controls:
12 * 1  - toggle between volume rendering methods
13 * 2 -  toggle between volume rendering modes ('volume', 'plane')
14 * [] - shift plane along plane normal
15 * {} - decrease/increase plane thickness
16 
17 * x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis
18 """
19 import sys
20 
21 import numpy as np
22 
23 from vispy import app, scene, io
24 from vispy.visuals.transforms import STTransform
25 
26 # Read volume
27 vol = np.load(io.load_data_file('volume/stent.npz'))['arr_0']
28 
29 # Prepare canvas
30 canvas = scene.SceneCanvas(keys='interactive', show=True)
31 view = canvas.central_widget.add_view()
32 
33 # Create the volume visual for plane rendering
34 plane = scene.visuals.Volume(
35     vol,
36     parent=view.scene,
37     raycasting_mode='plane',
38     method='mip',
39     plane_thickness=3.0,
40     plane_position=(128, 60, 64),
41     plane_normal=(1, 0, 0),
42 )
43 
44 volume = scene.visuals.Volume(
45     vol,
46     parent=view.scene,
47     raycasting_mode='volume',
48     method='mip',
49 )
50 volume.set_gl_state('additive')
51 volume.opacity = 0.25
52 
53 # Create a camera
54 cam = scene.cameras.TurntableCamera(
55     parent=view.scene, fov=60.0, azimuth=-42.0, elevation=30.0
56 )
57 view.camera = cam
58 
59 # Create an XYZAxis visual
60 axis = scene.visuals.XYZAxis(parent=view)
61 s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))
62 affine = s.as_matrix()
63 axis.transform = affine
64 
65 
66 def update_axis_visual():
67     """Sync XYZAxis visual with camera angles"""
68     axis.transform.reset()
69 
70     axis.transform.rotate(cam.roll, (0, 0, 1))
71     axis.transform.rotate(cam.elevation, (1, 0, 0))
72     axis.transform.rotate(cam.azimuth, (0, 1, 0))
73     axis.transform.scale((50, 50, 0.001))
74     axis.transform.translate((50., 50.))
75 
76     axis.update()
77 
78 
79 update_axis_visual()
80 
81 
82 @canvas.events.mouse_move.connect
83 def on_mouse_move(event):
84     if event.button == 1 and event.is_dragging:
85         update_axis_visual()
86 
87 
88 # Implement key presses
89 @canvas.events.key_press.connect
90 def on_key_press(event):
91     if event.text == '1':
92         methods = ['mip', 'average']
93         method = methods[(methods.index(plane.method) + 1) % 2]
94         print("Volume render method: %s" % method)
95         plane.method = method
96     elif event.text == '2':
97         modes = ['volume', 'plane']
98         if plane.raycasting_mode == modes[0]:
99             plane.raycasting_mode = modes[1]
100             print(modes[1])
101         else:
102             plane.raycasting_mode = modes[0]
103             print(modes[0])
104     elif event.text != '' and event.text in '{}':
105         t = -1 if event.text == '{' else 1
106         plane.plane_thickness += t
107         plane.plane_thickness += t
108         print(f"plane thickness: {plane.plane_thickness}")
109     elif event.text != '' and event.text in '[]':
110         shift = plane.plane_normal / np.linalg.norm(plane.plane_normal)
111         if event.text == '[':
112             plane.plane_position -= 2 * shift
113         elif event.text == ']':
114             plane.plane_position += 2 * shift
115         print(f"plane position: {plane.plane_position}")
116 
117     elif event.text == 'x':
118         plane.plane_normal = [0, 0, 1]
119     elif event.text == 'y':
120         plane.plane_normal = [0, 1, 0]
121     elif event.text == 'z':
122         plane.plane_normal = [1, 0, 0]
123     elif event.text == 'o':
124         plane.plane_normal = [1, 1, 1]
125 
126 
127 def move_plane(event):
128     z_pos = plane.plane_position[0]
129     if z_pos < 32:
130         plane.plane_position = plane.plane_position + [1, 0, 0]
131     elif 32 < z_pos <= 220:
132         plane.plane_position = plane.plane_position - [1, 0, 0]
133     else:
134         plane.plane_position = (220, 64, 64)
135 
136 
137 timer = app.Timer('auto', connect=move_plane, start=True)
138 
139 if __name__ == '__main__':
140     canvas.show()
141     print(__doc__)
142     if sys.flags.interactive == 0:
143         plane.plane_position = (220, 64, 64)
144         app.run()
145
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/examples/scene/volume_plane.py b/examples/scene/volume_plane.py
--- a/examples/scene/volume_plane.py
+++ b/examples/scene/volume_plane.py
@@ -13,6 +13,7 @@
 * 2 -  toggle between volume rendering modes ('volume', 'plane')
 * [] - shift plane along plane normal
 * {} - decrease/increase plane thickness
+* Spacebar - stop/start animation
 
 * x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis
 """
@@ -113,7 +114,6 @@
         elif event.text == ']':
             plane.plane_position += 2 * shift
         print(f"plane position: {plane.plane_position}")
-
     elif event.text == 'x':
         plane.plane_normal = [0, 0, 1]
     elif event.text == 'y':
@@ -122,6 +122,11 @@
         plane.plane_normal = [1, 0, 0]
     elif event.text == 'o':
         plane.plane_normal = [1, 1, 1]
+    elif event.text == ' ':
+        if timer.running:
+            timer.stop()
+        else:
+            timer.start()
 
 
 def move_plane(event):

{"golden_diff": "diff --git a/examples/scene/volume_plane.py b/examples/scene/volume_plane.py\n--- a/examples/scene/volume_plane.py\n+++ b/examples/scene/volume_plane.py\n@@ -13,6 +13,7 @@\n * 2 -  toggle between volume rendering modes ('volume', 'plane')\n * [] - shift plane along plane normal\n * {} - decrease/increase plane thickness\n+* Spacebar - stop/start animation\n \n * x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis\n \"\"\"\n@@ -113,7 +114,6 @@\n         elif event.text == ']':\n             plane.plane_position += 2 * shift\n         print(f\"plane position: {plane.plane_position}\")\n-\n     elif event.text == 'x':\n         plane.plane_normal = [0, 0, 1]\n     elif event.text == 'y':\n@@ -122,6 +122,11 @@\n         plane.plane_normal = [1, 0, 0]\n     elif event.text == 'o':\n         plane.plane_normal = [1, 1, 1]\n+    elif event.text == ' ':\n+        if timer.running:\n+            timer.stop()\n+        else:\n+            timer.start()\n \n \n def move_plane(event):\n", "issue": "Update volume_plane.py to allow timer to be stopped\nI noticed the volume_plane.py has keyboard handling for moving the plane through the volume, but it doesn't allow you to stop the animation so these events don't actually mean much.\r\n\r\n@alisterburt do you have time to fix this? Tradition is to use the spacebar (`\" \"`) to toggle the timer start/stop\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# -----------------------------------------------------------------------------\n# Copyright (c) Vispy Development Team. All Rights Reserved.\n# Distributed under the (new) BSD License. See LICENSE.txt for more info.\n# -----------------------------------------------------------------------------\n# vispy: gallery 10:200:5\n\"\"\"\nRendering Planes through 3D Data\n================================\n\nControls:\n* 1  - toggle between volume rendering methods\n* 2 -  toggle between volume rendering modes ('volume', 'plane')\n* [] - shift plane along plane normal\n* {} - decrease/increase plane thickness\n\n* x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis\n\"\"\"\nimport sys\n\nimport numpy as np\n\nfrom vispy import app, scene, io\nfrom vispy.visuals.transforms import STTransform\n\n# Read volume\nvol = np.load(io.load_data_file('volume/stent.npz'))['arr_0']\n\n# Prepare canvas\ncanvas = scene.SceneCanvas(keys='interactive', show=True)\nview = canvas.central_widget.add_view()\n\n# Create the volume visual for plane rendering\nplane = scene.visuals.Volume(\n    vol,\n    parent=view.scene,\n    raycasting_mode='plane',\n    method='mip',\n    plane_thickness=3.0,\n    plane_position=(128, 60, 64),\n    plane_normal=(1, 0, 0),\n)\n\nvolume = scene.visuals.Volume(\n    vol,\n    parent=view.scene,\n    raycasting_mode='volume',\n    method='mip',\n)\nvolume.set_gl_state('additive')\nvolume.opacity = 0.25\n\n# Create a camera\ncam = scene.cameras.TurntableCamera(\n    parent=view.scene, fov=60.0, azimuth=-42.0, elevation=30.0\n)\nview.camera = cam\n\n# Create an XYZAxis visual\naxis = scene.visuals.XYZAxis(parent=view)\ns = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))\naffine = s.as_matrix()\naxis.transform = affine\n\n\ndef update_axis_visual():\n    \"\"\"Sync XYZAxis visual with camera angles\"\"\"\n    axis.transform.reset()\n\n    axis.transform.rotate(cam.roll, (0, 0, 1))\n    axis.transform.rotate(cam.elevation, (1, 0, 0))\n    axis.transform.rotate(cam.azimuth, (0, 1, 0))\n    axis.transform.scale((50, 50, 0.001))\n    axis.transform.translate((50., 50.))\n\n    axis.update()\n\n\nupdate_axis_visual()\n\n\n@canvas.events.mouse_move.connect\ndef on_mouse_move(event):\n    if event.button == 1 and event.is_dragging:\n        update_axis_visual()\n\n\n# Implement key presses\n@canvas.events.key_press.connect\ndef on_key_press(event):\n    if event.text == '1':\n        methods = ['mip', 'average']\n        method = methods[(methods.index(plane.method) + 1) % 2]\n        print(\"Volume render method: %s\" % method)\n        plane.method = method\n    elif event.text == '2':\n        modes = ['volume', 'plane']\n        if plane.raycasting_mode == modes[0]:\n            plane.raycasting_mode = modes[1]\n            print(modes[1])\n        else:\n            plane.raycasting_mode = modes[0]\n            print(modes[0])\n    elif event.text != '' and event.text in '{}':\n        t = -1 if event.text == '{' else 1\n        plane.plane_thickness += t\n        plane.plane_thickness += t\n        print(f\"plane thickness: {plane.plane_thickness}\")\n    elif event.text != '' and event.text in '[]':\n        shift = plane.plane_normal / np.linalg.norm(plane.plane_normal)\n        if event.text == '[':\n            plane.plane_position -= 2 * shift\n        elif event.text == ']':\n            plane.plane_position += 2 * shift\n        print(f\"plane position: {plane.plane_position}\")\n\n    elif event.text == 'x':\n        plane.plane_normal = [0, 0, 1]\n    elif event.text == 'y':\n        plane.plane_normal = [0, 1, 0]\n    elif event.text == 'z':\n        plane.plane_normal = [1, 0, 0]\n    elif event.text == 'o':\n        plane.plane_normal = [1, 1, 1]\n\n\ndef move_plane(event):\n    z_pos = plane.plane_position[0]\n    if z_pos < 32:\n        plane.plane_position = plane.plane_position + [1, 0, 0]\n    elif 32 < z_pos <= 220:\n        plane.plane_position = plane.plane_position - [1, 0, 0]\n    else:\n        plane.plane_position = (220, 64, 64)\n\n\ntimer = app.Timer('auto', connect=move_plane, start=True)\n\nif __name__ == '__main__':\n    canvas.show()\n    print(__doc__)\n    if sys.flags.interactive == 0:\n        plane.plane_position = (220, 64, 64)\n        app.run()\n", "path": "examples/scene/volume_plane.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# -----------------------------------------------------------------------------\n# Copyright (c) Vispy Development Team. All Rights Reserved.\n# Distributed under the (new) BSD License. See LICENSE.txt for more info.\n# -----------------------------------------------------------------------------\n# vispy: gallery 10:200:5\n\"\"\"\nRendering Planes through 3D Data\n================================\n\nControls:\n* 1  - toggle between volume rendering methods\n* 2 -  toggle between volume rendering modes ('volume', 'plane')\n* [] - shift plane along plane normal\n* {} - decrease/increase plane thickness\n* Spacebar - stop/start animation\n\n* x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis\n\"\"\"\nimport sys\n\nimport numpy as np\n\nfrom vispy import app, scene, io\nfrom vispy.visuals.transforms import STTransform\n\n# Read volume\nvol = np.load(io.load_data_file('volume/stent.npz'))['arr_0']\n\n# Prepare canvas\ncanvas = scene.SceneCanvas(keys='interactive', show=True)\nview = canvas.central_widget.add_view()\n\n# Create the volume visual for plane rendering\nplane = scene.visuals.Volume(\n    vol,\n    parent=view.scene,\n    raycasting_mode='plane',\n    method='mip',\n    plane_thickness=3.0,\n    plane_position=(128, 60, 64),\n    plane_normal=(1, 0, 0),\n)\n\nvolume = scene.visuals.Volume(\n    vol,\n    parent=view.scene,\n    raycasting_mode='volume',\n    method='mip',\n)\nvolume.set_gl_state('additive')\nvolume.opacity = 0.25\n\n# Create a camera\ncam = scene.cameras.TurntableCamera(\n    parent=view.scene, fov=60.0, azimuth=-42.0, elevation=30.0\n)\nview.camera = cam\n\n# Create an XYZAxis visual\naxis = scene.visuals.XYZAxis(parent=view)\ns = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))\naffine = s.as_matrix()\naxis.transform = affine\n\n\ndef update_axis_visual():\n    \"\"\"Sync XYZAxis visual with camera angles\"\"\"\n    axis.transform.reset()\n\n    axis.transform.rotate(cam.roll, (0, 0, 1))\n    axis.transform.rotate(cam.elevation, (1, 0, 0))\n    axis.transform.rotate(cam.azimuth, (0, 1, 0))\n    axis.transform.scale((50, 50, 0.001))\n    axis.transform.translate((50., 50.))\n\n    axis.update()\n\n\nupdate_axis_visual()\n\n\n@canvas.events.mouse_move.connect\ndef on_mouse_move(event):\n    if event.button == 1 and event.is_dragging:\n        update_axis_visual()\n\n\n# Implement key presses\n@canvas.events.key_press.connect\ndef on_key_press(event):\n    if event.text == '1':\n        methods = ['mip', 'average']\n        method = methods[(methods.index(plane.method) + 1) % 2]\n        print(\"Volume render method: %s\" % method)\n        plane.method = method\n    elif event.text == '2':\n        modes = ['volume', 'plane']\n        if plane.raycasting_mode == modes[0]:\n            plane.raycasting_mode = modes[1]\n            print(modes[1])\n        else:\n            plane.raycasting_mode = modes[0]\n            print(modes[0])\n    elif event.text != '' and event.text in '{}':\n        t = -1 if event.text == '{' else 1\n        plane.plane_thickness += t\n        plane.plane_thickness += t\n        print(f\"plane thickness: {plane.plane_thickness}\")\n    elif event.text != '' and event.text in '[]':\n        shift = plane.plane_normal / np.linalg.norm(plane.plane_normal)\n        if event.text == '[':\n            plane.plane_position -= 2 * shift\n        elif event.text == ']':\n            plane.plane_position += 2 * shift\n        print(f\"plane position: {plane.plane_position}\")\n    elif event.text == 'x':\n        plane.plane_normal = [0, 0, 1]\n    elif event.text == 'y':\n        plane.plane_normal = [0, 1, 0]\n    elif event.text == 'z':\n        plane.plane_normal = [1, 0, 0]\n    elif event.text == 'o':\n        plane.plane_normal = [1, 1, 1]\n    elif event.text == ' ':\n        if timer.running:\n            timer.stop()\n        else:\n            timer.start()\n\n\ndef move_plane(event):\n    z_pos = plane.plane_position[0]\n    if z_pos < 32:\n        plane.plane_position = plane.plane_position + [1, 0, 0]\n    elif 32 < z_pos <= 220:\n        plane.plane_position = plane.plane_position - [1, 0, 0]\n    else:\n        plane.plane_position = (220, 64, 64)\n\n\ntimer = app.Timer('auto', connect=move_plane, start=True)\n\nif __name__ == '__main__':\n    canvas.show()\n    print(__doc__)\n    if sys.flags.interactive == 0:\n        plane.plane_position = (220, 64, 64)\n        app.run()\n", "path": "examples/scene/volume_plane.py"}]}