Changeset - 60b49f6c2027
[Not reviewed]
default
0 2 1
drewp@bigasterisk.com - 16 years ago 2008-09-01 00:41:29
drewp@bigasterisk.com
start porting lightsim to qt
3 files changed with 310 insertions and 68 deletions:
0 comments (0 inline, 0 general)
bin/lightsim
Show inline comments
 
@@ -2,31 +2,30 @@
 

	
 
from __future__ import division
 
import run_local
 
import xmlrpclib, sys, logging
 

	
 
sys.path.append("lib")
 
import qt4reactor
 
qt4reactor.install()
 

	
 
from rdflib import Literal, URIRef
 
from twisted.internet import reactor, tksupport
 
from twisted.internet import reactor
 
from twisted.internet.task import LoopingCall
 
from twisted.web.xmlrpc import Proxy
 
import xmlrpclib, sys, logging
 
from louie import dispatcher
 
from PyQt4 import QtCore, QtGui, QtOpenGL
 
from OpenGL.GL import *
 
from OpenGL.GLU import *
 

	
 
from light9 import networking, Patch, showconfig, dmxclient, updatefreq, prof
 
from light9.namespaces import L9
 
from lightsim.openglsim import Surface
 

	
 
log = logging.getLogger()
 
logging.basicConfig(format="%(asctime)s %(levelname)-5s %(name)s %(filename)s:%(lineno)d: %(message)s")
 
log.setLevel(logging.DEBUG)
 
import Tkinter as tk
 
from light9 import networking, Patch, showconfig, dmxclient, updatefreq, prof
 
from light9.namespaces import L9
 
from louie import dispatcher
 

	
 
try:
 
    from OpenGL import Tk as Togl
 
    from OpenGL.GL import *
 
except ImportError:
 
    sys.path.append("/usr/lib/python2.4/site-packages/OpenGL/Tk/linux2-tk8.4")
 
    from OpenGL.GL import *
 
    import Togl
 
  
 
from lightsim.openglsim import Surface
 

	
 
def poll(graph, serv, pollFreq):
 
def poll(graph, serv, pollFreq, oglSurface):
 
    pollFreq.update()
 
    dispatcher.send("status", key="pollFreq", value=str(pollFreq))
 
    d = serv.callRemote("currentlevels", dmxclient._id)
 
@@ -45,11 +44,11 @@ def poll(graph, serv, pollFreq):
 
                for imgPath in graph.objects(lyr, L9['path']):
 
                    level[str(imgPath)] = lev
 

	
 
        ogl.newLevels(levels=level)
 
        oglSurface.newLevels(levels=level)
 
    d.addCallback(received)
 
    return d
 

	
 
class StatusKeys(tk.Frame):
 
class StatusKeys(object):
 
    # watch out- this might be an accidental redo of what curvecalc
 
    # has. Or, maybe CC should use this obj
 
    def __init__(self, master):
 
@@ -69,31 +68,51 @@ class StatusKeys(tk.Frame):
 
            row, lab = self.row[key]
 
            lab.config(text=value)
 

	
 
root = tk.Frame()
 
root.pack(expand=True, fill='both')
 
pollFreq = updatefreq.Updatefreq(samples=5)
 
graph = showconfig.getGraph()
 
filenames = []
 
for lyr in graph.objects(None, L9['previewLayer']):
 
    for p in graph.objects(lyr, L9['path']):
 
        filenames.append(str(p))
 

	
 
ogl = Surface(root, filenames, width=120, height=80, imgRescaleTo=128)
 
ogl.pack(side='top', expand=True, fill='both')
 

	
 
sk = StatusKeys(root)
 
sk.pack(side='top', fill='x')
 

	
 

	
 
class Window(QtGui.QMainWindow):
 
    def __init__(self, filenames):
 
        QtGui.QMainWindow.__init__(self, None)
 

	
 
serv = Proxy(networking.dmxServerUrl())
 
LoopingCall(poll, graph, serv, pollFreq).start(.1)
 
        self.w = QtGui.QWidget()
 
        self.setCentralWidget(self.w)
 
        
 
        self.glWidget = Surface(self, filenames,
 
                                width=120*3, height=80*3,
 
                                imgRescaleTo=128)
 

	
 
        self.glWidget.newLevels(levels={
 
            "lightsim/skyline/front-left.png" : .4,
 
            "lightsim/skyline/front-right.png" : .8})
 

	
 
        mainLayout = QtGui.QHBoxLayout()
 
        mainLayout.addWidget(self.glWidget)
 
        self.w.setLayout(mainLayout)
 

	
 
        self.setWindowTitle(dmxclient._id)
 

	
 
top = root.winfo_toplevel()
 
top.wm_title(dmxclient._id)
 
top.bind("<Control-Key-q>",lambda ev: reactor.stop)
 
top.bind("<Destroy>",lambda ev: reactor.stop)
 
top.protocol('WM_DELETE_WINDOW', reactor.stop)
 
tksupport.install(ogl, ms=20)
 
if __name__ == '__main__':
 
    app = reactor.qApp
 

	
 
    graph = showconfig.getGraph()
 
    filenames = []
 
    for lyr in graph.objects(None, L9['previewLayer']):
 
        for p in graph.objects(lyr, L9['path']):
 
            filenames.append(str(p))
 
    filenames.append("lightsim/skyline/front-left.png") # test
 
    filenames.append("lightsim/skyline/front-right.png") # test
 

	
 
prof.run(reactor.run, profile=False)
 
    window = Window(filenames)
 
    window.show()
 

	
 
    serv = Proxy(networking.dmxServerUrl())
 
    pollFreq = updatefreq.Updatefreq(samples=5)
 
    LoopingCall(poll, graph, serv, pollFreq, window.glWidget).start(.1)
 

	
 
    reactor.run()
 

	
 

	
 
######################################################
 
#sk = StatusKeys(root)
 
#sk.pack(side='top', fill='x')
 
#prof.run(reactor.run, profile=False)
lib/qt4reactor.py
Show inline comments
 
new file 100644
 
# http://twistedmatrix.com/trac/browser/sandbox/therve/qt4reactor.py
 
# with some fixes by drewp
 

	
 
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
# See LICENSE for details.
 

	
 

	
 
"""
 
This module provides support for Twisted to interact with the PyQt mainloop.
 

	
 
In order to use this support, simply do the following::
 

	
 
    |  import qt4reactor
 
    |  qt4reactor.install()
 

	
 
Then use twisted.internet APIs as usual.  The other methods here are not
 
intended to be called directly.
 

	
 
API Stability: stable
 

	
 
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
 
Port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
 
"""
 

	
 
__all__ = ['install']
 

	
 

	
 
import sys
 

	
 
from zope.interface import implements
 

	
 
from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer
 
from PyQt4.QtGui import QApplication
 

	
 
from twisted.internet.interfaces import IReactorFDSet
 
from twisted.python import log
 
from twisted.internet.posixbase import PosixReactorBase
 

	
 

	
 

	
 
class TwistedSocketNotifier(QSocketNotifier):
 
    """
 
    Connection between an fd event and reader/writer callbacks.
 
    """
 

	
 
    def __init__(self, reactor, watcher, type):
 
        QSocketNotifier.__init__(self, watcher.fileno(), type)
 
        self.reactor = reactor
 
        self.watcher = watcher
 
        self.fn = None
 
        if type == QSocketNotifier.Read:
 
            self.fn = self.read
 
        elif type == QSocketNotifier.Write:
 
            self.fn = self.write
 
        QObject.connect(self, SIGNAL("activated(int)"), self.fn)
 

	
 

	
 
    def shutdown(self):
 
        QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
 
        self.setEnabled(0)
 
        self.fn = self.watcher = None
 

	
 

	
 
    def read(self, sock):
 
        w = self.watcher
 
        def _read():
 
            why = None
 
            try:
 
                why = w.doRead()
 
            except:
 
                log.err()
 
                why = sys.exc_info()[1]
 
            if why:
 
                self.reactor._disconnectSelectable(w, why, True)
 
        log.callWithLogger(w, _read)
 
        self.reactor.simulate()
 

	
 

	
 
    def write(self, sock):
 
        w = self.watcher
 
        def _write():
 
            why = None
 
            self.setEnabled(0)
 
            try:
 
                why = w.doWrite()
 
            except:
 
                log.err()
 
                why = sys.exc_info()[1]
 
            if why:
 
                self.reactor._disconnectSelectable(w, why, False)
 
            elif self.watcher:
 
                self.setEnabled(1)
 
        log.callWithLogger(w, _write)
 
        self.reactor.simulate()
 

	
 

	
 

	
 
class QTReactor(PosixReactorBase):
 
    """
 
    Qt based reactor.
 
    """
 
    implements(IReactorFDSet)
 

	
 
    # Reference to a DelayedCall for self.crash() when the reactor is
 
    # entered through .iterate()
 
    _crashCall = None
 

	
 
    _timer = None
 

	
 
    def __init__(self, app=None):
 
        self._reads = {}
 
        self._writes = {}
 
        if app is None:
 
            app = QApplication([])
 
        self.qApp = app
 
        PosixReactorBase.__init__(self)
 
        self.addSystemEventTrigger('after', 'shutdown', self.cleanup)
 
            
 

	
 

	
 
    def addReader(self, reader):
 
        if not reader in self._reads:
 
            self._reads[reader] = TwistedSocketNotifier(self, reader,
 
                                                       QSocketNotifier.Read)
 

	
 

	
 
    def addWriter(self, writer):
 
        if not writer in self._writes:
 
            self._writes[writer] = TwistedSocketNotifier(self, writer,
 
                                                        QSocketNotifier.Write)
 

	
 

	
 
    def removeReader(self, reader):
 
        if reader in self._reads:
 
            self._reads[reader].shutdown()
 
            del self._reads[reader]
 

	
 

	
 
    def removeWriter(self, writer):
 
        if writer in self._writes:
 
            self._writes[writer].shutdown()
 
            del self._writes[writer]
 

	
 

	
 
    def removeAll(self):
 
        return self._removeAll(self._reads, self._writes)
 

	
 

	
 
    def getReaders(self):
 
        return self._reads.keys()
 

	
 

	
 
    def getWriters(self):
 
        return self._writes.keys()
 

	
 

	
 
    def simulate(self):
 
        self._lastTimer = self._timer # put off the __del__
 
        if self._timer is not None:
 
            self._timer.stop()
 
            self._timer = None
 

	
 
        if not self.running:
 
            self.qApp.exit()
 
            return
 
        self.runUntilCurrent()
 

	
 
        if self._crashCall is not None:
 
            self._crashCall.reset(0)
 

	
 
        timeout = self.timeout()
 
        if timeout is None:
 
            timeout = 1.0
 
        timeout = min(timeout, 0.01) * 1010
 

	
 
        if self._timer is None:
 
            self._timer = QTimer()
 
            self._timer.setObjectName("simulateTimer")
 
            QObject.connect(self._timer, SIGNAL("timeout()"), self.simulate)
 
        self._timer.start(timeout)
 

	
 
    def cleanup(self):
 
        if self._timer is not None:
 
            self._timer.stop()
 
            self._timer = None
 

	
 

	
 
    def iterate(self, delay=0.0):
 
        self._crashCall = self.callLater(delay, self._crash)
 
        self.run()
 

	
 

	
 
    def mainLoop(self):
 
        self.simulate()
 
        self.qApp.exec_()
 

	
 

	
 
    def _crash(self):
 
        if self._crashCall is not None:
 
            if self._crashCall.active():
 
                self._crashCall.cancel()
 
            self._crashCall = None
 
        self.running = False
 

	
 

	
 

	
 
def install(app=None):
 
    """
 
    Configure the twisted mainloop to be run inside the qt mainloop.
 
    """
 
    from twisted.internet import main
 
    reactor = QTReactor(app=app)
 
    main.installReactor(reactor)
lightsim/openglsim.py
Show inline comments
 
@@ -7,14 +7,9 @@ import numarray as num
 
import Tkinter as tk
 
import Image
 
from louie import dispatcher
 
try:
 
    from OpenGL import Tk as Togl
 
    from OpenGL.GL import *
 
except ImportError:
 
    sys.path.append("/usr/lib/python2.4/site-packages/OpenGL/Tk/linux2-tk8.4")
 
    from OpenGL.GL import *
 
    import Togl
 
  
 

	
 
from PyQt4 import QtCore, QtOpenGL
 
from OpenGL.GL import *
 

	
 
def xxxdrawWithAlpha(imgString, w, h, alpha):
 
    # this one should be faster because GL does the alpha adjust, but
 
@@ -26,22 +21,21 @@ def xxxdrawWithAlpha(imgString, w, h, al
 
    #glBlendColor(1, 1, 1, mag) # needs ARB_imaging
 
    glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, imgString)
 

	
 
class Surface(Togl.Opengl):
 
class Surface(QtOpenGL.QGLWidget):
 
    """widget that adds multiple image files together with adjustable scales"""
 
    def __init__(self, master, filenames, width=512, height=270,
 
    def __init__(self, parent, filenames, width=512, height=270,
 
                 imgRescaleTo=None):
 
        """
 
        imgRescaleTo can be a length of pixels to reduce all the input
 
        images into. Try 64 for a low res drawing.
 
        """
 
        Togl.Opengl.__init__(self, master=master, width=width,
 
                             height=height, double=True, depth=0)
 
        self.width, self.height = width, height
 
        QtOpenGL.QGLWidget.__init__(self, parent)
 

	
 
        self.levels = {} # filename : brightness
 
  
 
        self.image = {} # filename : imgstr
 
        for filename in filenames:
 
            print "open", filename
 
            im = Image.open(filename)
 
            if imgRescaleTo:
 
                im.thumbnail((imgRescaleTo, imgRescaleTo))
 
@@ -50,20 +44,22 @@ class Surface(Togl.Opengl):
 
            self.imageHeight = im.size[1]
 
            self.image[filename] = im.convert("RGBA").tostring()
 
  
 
        self.set_centerpoint(0, 0, 0)
 
  
 
        #self.set_centerpoint(0, 0, 0)
 

	
 
    def initializeGL(self): 
 
        glDisable(GL_CULL_FACE)
 
        glShadeModel(GL_FLAT)
 
        print 'GL_ARB_imaging', 'GL_ARB_imaging' in glGetString(GL_EXTENSIONS)
 
        import OpenGL
 
        print OpenGL.__version__
 

	
 
        self.bind("<Configure>", self.configure)
 
       
 
#    def minimumSizeHint(self):
 
#        return QtCore.QSize(512, 512)
 

	
 
    def configure(self, ev):
 
        self.width, self.height = ev.width, ev.height
 
  
 
    def redraw(self, event=None):
 
#    def sizeHint(self):
 
#        return QtCore.QSize(512, 512)
 

	
 
    def paintGL(self):
 
        """you set self.levels to dict and call tkRedraw"""
 
        assert 'GL_ARB_imaging' in glGetString(GL_EXTENSIONS).split()
 
        start = time.time()
 
@@ -80,7 +76,6 @@ class Surface(Togl.Opengl):
 
        # drawing to glAccum might be good
 
        layerTimes = []
 
        for filename, mag in self.levels.items():
 
            #print "pic %s at %f" % (filename, mag)
 
            t = time.time()
 
            self.drawWithAlpha(self.image[filename],
 
                               self.imageWidth, self.imageHeight, mag)
 
@@ -109,8 +104,8 @@ class Surface(Togl.Opengl):
 
        # this right now.
 
        #glBlendFunc(GL_CONSTANT_COLOR, GL_ONE)
 
        #glBlendColor(.8, .5, .5, .5)
 
        
 
        glPixelZoom(self.width / w, self.height / h)
 

	
 
        glPixelZoom(self.width() / w, self.height() / h)
 
        glDrawPixels(w, h,
 
                     GL_RGBA, GL_UNSIGNED_BYTE, ar.tostring())
 
        #print "  draw", time.time() - t
 
@@ -118,8 +113,23 @@ class Surface(Togl.Opengl):
 
    def newLevels(self, event=None, levels=None):
 
        if levels != self.levels:
 
            self.levels = levels
 
            self.tkRedraw()
 
  
 
            self.updateGL()
 

	
 

	
 
##     def mousePressEvent(self, event):
 
##         self.lastPos = QtCore.QPoint(event.pos())
 

	
 
##     def mouseMoveEvent(self, event):
 
##         dx = event.x() - self.lastPos.x()
 
##         dy = event.y() - self.lastPos.y()
 
##         rot = (.25*dy, .25*dx, 0)
 
##         if event.buttons() & QtCore.Qt.LeftButton:
 
##             self.emit(QtCore.SIGNAL("rotationChanged"), rot)
 
##         elif event.buttons() & QtCore.Qt.MidButton:
 
##             self.emit(QtCore.SIGNAL("camDistChanged"), .01*dy)            
 

	
 
##         self.lastPos = QtCore.QPoint(event.pos())
 

	
 
def main():
 
    root = tk.Frame()
 
    root.pack(expand=True, fill='both')
0 comments (0 inline, 0 general)