Repository: vispy/vispy

Issue:
gloo cannot be used without a canvas?
The discussion on the list seems to indicate this is the case:

https://groups.google.com/forum/#!topic/vispy/L1EFzNreMgA

@almarklein is there a workaround? Can we tell it somehow not to use features of `Canvas`? It seems like we broke orthogonality a bit with the GLIR code. If there is already a way to do this, then we should fix the `assert` statement with a more informative error saying how to use `gloo` outside the `app` system.



Discussion:

Resolving patch:
diff --git a/vispy/gloo/context.py b/vispy/gloo/context.py
--- a/vispy/gloo/context.py
+++ b/vispy/gloo/context.py
@@ -212,3 +212,21 @@ def ref(self):
             return ref
         else:
             raise RuntimeError('No reference for available for GLShared')
+
+
+class FakeCanvas(object):
+    """ Fake canvas to allow using gloo without vispy.app
+    
+    Instantiate this class to collect GLIR commands from gloo
+    interactions. Call flush() in your draw event handler to execute
+    the commands in the active contect.
+    """
+    
+    def __init__(self):
+        self.context = GLContext()
+        set_current_canvas(self)
+    
+    def flush(self):
+        """ Flush commands. Call this after setting to context to current.
+        """
+        self.context.flush_commands()
diff --git a/vispy/gloo/wrappers.py b/vispy/gloo/wrappers.py
--- a/vispy/gloo/wrappers.py
+++ b/vispy/gloo/wrappers.py
@@ -570,7 +570,10 @@ def glir(self):
         """ The GLIR queue corresponding to the current canvas
         """
         canvas = get_current_canvas()
-        assert canvas is not None
+        if canvas is None:
+            msg = ("If you want to use gloo without vispy.app, " + 
+                   "use a gloo.context.FakeCanvas.")
+            raise RuntimeError('Gloo requires a Canvas to run.\n' + msg)
         return canvas.context.glir
 
 


Test patch:
diff --git a/vispy/gloo/tests/test_context.py b/vispy/gloo/tests/test_context.py
--- a/vispy/gloo/tests/test_context.py
+++ b/vispy/gloo/tests/test_context.py
@@ -5,6 +5,7 @@
 from vispy.testing import (assert_in, run_tests_if_main, assert_raises,
                            assert_equal, assert_not_equal)
 
+from vispy import gloo
 from vispy.gloo import (GLContext, get_default_config)
 
 
@@ -27,7 +28,7 @@ def __init__(self):
     def _vispy_set_current(self):
         self.set_current = True
 
-    
+
 def test_context_config():
     """ Test GLContext handling of config dict
     """
@@ -54,7 +55,7 @@ def test_context_config():
     # Passing crap should raise
     assert_raises(KeyError, GLContext, {'foo': 3})
     assert_raises(TypeError, GLContext, {'double_buffer': 'not_bool'})
-    
+
 
 def test_context_taking():
     """ Test GLContext ownership and taking
@@ -88,4 +89,31 @@ def get_canvas(c):
     assert_raises(RuntimeError, get_canvas, c)
 
 
+def test_gloo_without_app():
+    """ Test gloo without vispy.app (with FakeCanvas) """
+    
+    # Create dummy parser
+    class DummyParser(gloo.glir.BaseGlirParser):
+        def __init__(self):
+            self.commands = []
+        
+        def parse(self, commands):
+            self.commands.extend(commands)
+    
+    p = DummyParser()
+    
+    # Create fake canvas and attach our parser
+    c = gloo.context.FakeCanvas()
+    c.context.shared.parser = p
+    
+    # Do some commands
+    gloo.clear()
+    c.flush()
+    gloo.clear()
+    c.flush()
+    
+    assert len(p.commands) in (2, 3)  # there may be a CURRENT command
+    assert p.commands[-1][1] == 'glClear'
+
+
 run_tests_if_main()