Search code examples
pythonglutpyopengl

pyopengl glut input


After the window containing GLUT graphics appears, I would like to enter input in the terminal:

user@computer: python woop.py
# Now displaying a beautiful landscape
(cmd): season winter
# Now changing season to winter
(cmd): event meteor
# Now meteoring otherwise peaceful landscape
(cmd): season summer
# Now changing season to summer
(cmd): exit
#bye ^_^
user@computer:

Ideally I would like to integrate python cmd with GLUT's glutKeyboardFunc. My attempts failed (allows one or the other at once, not both. Also problems with whether the window or terminal had focus).

Here is some example code, which displays a spinning teapot. Currently, pressing 'm' will invoke meteory goodness (stub), but being able to enter e.g. "meteor 500" would be preferable.

#! /usr/bin/env python
''' 
Code is a reduced version of http://www.seethroughskin.com/blog/?p=771
'''
import OpenGL 
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import time, sys

class dizzyTea:

    global rotY

    def __init__(self):
        self.main()

    def InitGL(self,Width, Height):
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1.0)
        glShadeModel(GL_SMOOTH)  
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()       
        gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
        glMatrixMode(GL_MODELVIEW)     

    # The main drawing function. 
    def DrawGLScene(self):
        global rotY
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()                    # Reset The View 
        glTranslatef(-0.5, 0.0, -6.0)
        glRotatef(rotY,0.0,1.0,0.0)
        glutWireTeapot(1.0)
        glScalef(0.3,0.3,0.3)
        glutSwapBuffers()
        rotY += 1.0

    # The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)  
    def keyPressed(self,*args):

        # If escape is pressed, kill everything.
        if args[0] == '\x1b':
            sys.exit()
        elif args[0] == 'm':
            print "Now meteoring otherwise peaceful teapot"
            # meteor shenanigans

    def main(self):
        global window
        global rotY
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        glutInitWindowSize(640, 480)
        glutInitWindowPosition(0, 0)
        window = glutCreateWindow("Jeff Molofee's desecrated GL Code Tutorial")
        glutDisplayFunc(self.DrawGLScene)
        glutIdleFunc(self.DrawGLScene)
        glutKeyboardFunc(self.keyPressed)
        self.InitGL(800, 600)
        rotY = 0.0
        glutMainLoop()

if __name__ == "__main__":
    x = dizzyTea()

I can collect characters into a global string using glutKeyboardFunc, giving the same functional effect, however the user would be typing blind. "print somestring," allows printing on the same line, however the comma means the output does not get displayed whilst typing. Also "print '\b'" (backspace) doesn't work universally...

basically I don't want to have:

user@computer: python woop.py
# Now displaying a beautiful landscape
(cmd): s
(cmd): se
(cmd): sea
(cmd): seas
...etc

to type out one command

Restriction of using:

  • pyopengl
  • GLUT

(Though other answers are welcome for wayward future-people looking to solve a different problem)


Solution

  • # The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)  
    def keyPressed(self,*args):
        if args[0] == '\x08':
            self.keyCache = self.keyCache[:-1]
        elif args[0] == '\x1b':
            sys.exit()
        elif args[0] == 'm':
            print "Now meteoring otherwise peaceful teapot"
            # meteor shenanigans
        else:
            self.keyCache += args[0]
        sys.stdout.write(self.keyCache +"                                                  \r")#print "keypress: <",self.keyCache,">"
        sys.stdout.flush()
    

    And add a new class variable 'keyCache'.

    Then just use normal print flushing to write data to the same line. The only hacky bit is that you have to write several blank spaces after the cached keystrokes, otherwise when you use backspace the deleted elements will still be on the screen.

    Another alternative would be a parallel keyboard thread just to handle keystrokes from the command line. The big problem there is that glut doesn't provide a nice call back for when the window closes so you'd have to figure out an alternative way to kill your thread.