Search code examples
pythonopenglpygtkvpython

GTK window capture: VPython (OpenGL) application


Having read the documentation for VPython and GTK threading, it seems to me that it would be possible to embed VPython graphics within a gtk GUI. I know that it is possible with wx on Windows but I am on Linux and using PyGTK. Now, I have managed to get part of the way. I can embed a VPython window provided that it is spawned a separate process. What I would like is to embed it as a thread. The latter would make GUI events that control the OpenGL easier to implement -- via a thread instead of a socket and network calls.

Edit: Apparently nobody knows anything about this... Meh.

Here is the code I have. Uncomment the two commented out lines and comment a few obvious others and you can get to the process spawning code.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from visual import *
import threading
import Queue
import gtk
import pygtk
import re
import subprocess


class OPenGLThreadClass (threading.Thread):
    """Thread running the VPython code."""

    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.name = 'OpenGLThread'


    def run (self):
        gtk.threads_enter()
        self.scene = display.get_selected() 
        self.scene.title = 'OpenGL test'
        s = sphere()
        gtk.threads_leave()
        #P = subprocess.Popen(['python', 'opengl.py'])
        time.sleep(2)
        self.queue.put(self.find_window_id())
        self.queue.task_done()


    def find_window_id (self):
        """Gets the OpenGL window ID."""
        pattern = re.compile('0x[0-9abcdef]{7}')
        P = subprocess.Popen(['xwininfo', '-name', self.scene.title],
        #P = subprocess.Popen(['xwininfo', '-name', 'Visual WeldHead'],
                stdout=subprocess.PIPE)
        for line in P.stdout.readlines():
            match = pattern.findall(line)
            if len(match):
                ret = long(match[0], 16)
                print("OpenGL window id is %d (%s)" % (ret, hex(ret)))
                return ret


class GTKWindowThreadClass (threading.Thread):
    """Thread running the GTK code."""

    def __init__ (self, winID):
        threading.Thread.__init__(self)
        self.OpenGLWindowID = winID
        self.name = 'GTKThread'


    def run (self):
        """Draw the GTK GUI."""
        gtk.threads_enter()
        window = gtk.Window()
        window.show()
        socket = gtk.Socket()
        socket.show()
        window.add(socket)
        window.connect("destroy", lambda w: gtk.main_quit())
        print("Got winID as %d (%s)" % (self.OpenGLWindowID, hex(self.OpenGLWindowID)))
        socket.add_id(long(self.OpenGLWindowID))
        gtk.main()
        gtk.threads_leave()



def main ():
    thread = {}
    print("Embedding OpenGL/VPython into GTK GUI")
    queue = Queue.Queue()
    thread['OpenGL'] = OPenGLThreadClass(queue)
    thread['OpenGL'].start()
    winID = queue.get()
    print("Got winID as %d (%s)" % (winID, hex(winID)))
    gtk.gdk.threads_init()
    thread['GTK'] = GTKWindowThreadClass(winID)
    thread['GTK'].start()



if __name__ == "__main__":
    main()

Solution

  • This is the code that works in case anyone cares.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import subprocess
    import sys
    import os
    import re
    import time
    from visual import *
    
    
    def find_window_id (title):
        """Gets the OpenGL window ID."""
        pattern = re.compile('0x[0-9abcdef]{7}')
        proc = subprocess.Popen(['xwininfo', '-name', title],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        errors = proc.stderr.readlines()
        if errors:
            return None
        for line in proc.stdout.readlines():
            match = pattern.findall(line)
            if len(match):
                return long(match[0], 16)
        return None
    
    
    
    class Setting ():
        """VPython/OpenGL class."""
    
        def __init__ (self, w=256, h=256, title='OpenGL via VPython'):
            """Initiator."""
            self.width = w
            self.height = h
            self.title = title
            self.scene = display.get_selected() 
            self.scene.title = self.title
            self.scene.width = self.width
            self.scene.height = self.height
            self.sphere = sphere()
    
    
    
    class GTKDisplay ():
    
        def __init__ (self, winID):
            """Initiator: Draws the GTK GUI."""
            import gtk
            import pygtk
            self.OpenGLWindowID = winID
            window = gtk.Window()
            window.show()
            socket = gtk.Socket()
            socket.show()
            window.add(socket)
            window.connect("destroy", lambda w: gtk.main_quit())
            socket.add_id(long(self.OpenGLWindowID))
            gtk.main()
    
    
    
    def main ():
        """Main entry point."""
        name = 'sphere OpenGL window'
        child_pid = os.fork()
        if 0 == child_pid:
            sut = Setting(title=name)
        else:
            winID = None
            while not winID:
                time.sleep(.1)
                winID = find_window_id(name)
            try:
                gui = GTKDisplay(winID)
            except KeyboardInterrupt, err:
                print '\nAdieu monde cruel!'
    
    
    if __name__ == "__main__":
        main()
    

    Note: This does not work under Gnome but works under fvwm2. Go figure...