Panda in PyQt

Panda3D Forum Index -> Code Snippets Post new topic   Reply to topic
View previous topic :: View next topic  
Author Message
ZeroByte


Posts: 308

PostPosted: Sat May 24, 2008 11:58 am    Post subject: Panda in PyQt Reply with quote
Here's early code on getting Panda integrated with PyQt as a widget. Its really slow since converting the frame buffer into images PyQt can use is really slow, theres probably a better way though.

To run the demo you'll need PyQt4

Code:
from pandac.PandaModules import loadPrcFileData
#loadPrcFileData("", "window-type offscreen") # Set Panda to draw its main window in an offscreen buffer
# PyQt imports
from PyQt4 import QtGui,  QtCore
import sys
# Panda imports
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import GraphicsOutput,  Texture,  StringStream,  PNMImage
from direct.interval.LerpInterval import LerpHprInterval
from pandac.PandaModules import Point3
# Set up Panda environment
import direct.directbase.DirectStart

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.pandaTexture = texture
       
        # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop
        pandaTimer = QtCore.QTimer(self)
        self.connect(pandaTimer,  QtCore.SIGNAL("timeout()"), taskMgr.step)
        pandaTimer.start(0)
       
        # Setup another timer that redraws this widget in a specific FPS
        redrawTimer = QtCore.QTimer(self)
        self.connect(redrawTimer,  QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("update()"))
        redrawTimer.start(1000/60)
       
        # Setup a QLabel to paint the pixmap on
        self.paintSurface = QtGui.QLabel(self)
        self.paintSurface.setGeometry(0, 0, 800, 600)
        self.paintSurface.show()
       
        self.paintPixmap = QtGui.QPixmap(800, 600)
   
    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
       
        screenData = StringStream() # Used to pass the data as a string
        screenImage = PNMImage() # Converts the texture data into a format usable with Qt
       
        if self.pandaTexture.hasRamImage():
            print "Should draw yes?"
            self.pandaTexture.store(screenImage)
            screenImage.write(screenData, "test.ppm")
            self.paintPixmap.loadFromData(screenData.getData())
            self.paintSurface.setPixmap(self.paintPixmap)

class PandaHandler(DirectObject):
   
    def __init__(self):
        base.disableMouse()
        base.cam.setPos(0, -28, 6)
        self.testModel = loader.loadModel('panda')
        self.testModel.reparentTo(render)
        print self.testModel.getPos()
        self.rotateInterval = LerpHprInterval(self.testModel, 3, Point3(360, 0, 0))
        self.rotateInterval.loop()
       
        self.screenTexture = Texture()
        self.screenTexture.setMinfilter(Texture.FTLinear)
        base.win.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)

if __name__ == "__main__":   
    #lol teh hobo
    panHandler = PandaHandler()
   
    app = QtGui.QApplication(sys.argv)
    pandaWidget = QtPandaWidget(panHandler.screenTexture)
    pandaWidget.show()
   
    sys.exit(app.exec_())
ZeroByte


Posts: 308

PostPosted: Sat May 24, 2008 6:15 pm    Post subject: Reply with quote
Ooh, made some minor changes to the code and got masked windows in. Probably can't go by this route to get decent speed though. You can see the redraws on it, nasty.

Code changes, copy/paste friendly
Code:
...

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) # Setup the window so its frameless
        self.pandaTexture = texture

...

    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
       
        screenData = StringStream() # Used to pass the data as a string
        screenImage = PNMImage() # Converts the texture data into a format usable with Qt
       
        if self.pandaTexture.mightHaveRamImage(): #ambigious!
            print "Should draw yes?"
            self.pandaTexture.store(screenImage)
            print "Has alpha?",screenImage.hasAlpha()
            screenImage.write(screenData, "test.png")
            self.paintPixmap.loadFromData(screenData.getData())
            self.paintSurface.setPixmap(self.paintPixmap)
            self.setMask(self.paintPixmap.mask())
...


Picture!
ZeroByte


Posts: 308

PostPosted: Tue Jun 17, 2008 4:09 pm    Post subject: Reply with quote
Current state:

Drawing into PyQt windows is faster now without image conversion getting in the way. If you wished to make a PyQt app with a native panda component (for GUI's maybe?) this is a pretty good way to do it. Still not satisfied though as the alpha masking for the window still has to go through conversion and is therefore slow.

Still not satisfied though

Code:

from pandac.PandaModules import loadPrcFileData
#loadPrcFileData("", "window-type offscreen") # Set Panda to draw its main window in an offscreen buffer
# PyQt imports
from PyQt4 import QtGui,  QtCore
import sys
# Panda imports
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import GraphicsOutput,  Texture,  StringStream,  PNMImage
from direct.interval.LerpInterval import LerpHprInterval
from pandac.PandaModules import Point3
# Set up Panda environment
import direct.directbase.DirectStart
from struct import *

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) # Setup the window so its frameless
        self.pandaTexture = texture
       
        # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop
        pandaTimer = QtCore.QTimer(self)
        self.connect(pandaTimer,  QtCore.SIGNAL("timeout()"), taskMgr.step)
        pandaTimer.start(0)
       
        # Setup another timer that redraws this widget in a specific FPS
        redrawTimer = QtCore.QTimer(self)
        self.connect(redrawTimer,  QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("update()"))
        redrawTimer.start(1000/60)
       
        self.paintSurface = QtGui.QPainter()
        self.rotate = QtGui.QTransform()
        self.rotate.rotate(180)
       
        self.out_image = QtGui.QImage()
   
    def showEvent(self, event):
        self.desktopBg = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop ().winId(), \
                self.geometry().x(),self.geometry().y(), self.rect().width(), self.rect().height())
   
    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
        if self.pandaTexture.mightHaveRamImage():
            self.pandaTexture.setFormat(Texture.FRgba32)
            #print "Should draw yes?"
            data = self.pandaTexture.getRamImage().getData()
            img = QtGui.QImage(data, self.pandaTexture.getXSize(), self.pandaTexture.getYSize(), QtGui.QImage.Format_ARGB32).mirrored()
            self.paintSurface.begin(self)
            self.paintSurface.drawPixmap(0, 0, self.desktopBg)
            self.paintSurface.drawImage(0, 0, img)
            self.paintSurface.end()
            pixmap = QtGui.QPixmap.fromImage(img)
            self.setMask(pixmap.mask())

class PandaHandler(DirectObject):
   
    def __init__(self):
        base.disableMouse()
        base.cam.setPos(0, -28, 6)
        self.testModel = loader.loadModel('panda')
        self.testModel.reparentTo(render)
        self.rotateInterval = LerpHprInterval(self.testModel, 3, Point3(360, 0, 0))
        self.rotateInterval.loop()
       
        self.screenTexture = Texture()
        self.screenTexture.setMinfilter(Texture.FTLinear)
        self.screenTexture.setFormat(Texture.FRgba32)
        print "Format is", self.screenTexture.getFormat()
        base.win.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)

if __name__ == "__main__":   
    #lol teh hobo
    panHandler = PandaHandler()
   
    app = QtGui.QApplication(sys.argv)
    pandaWidget = QtPandaWidget(panHandler.screenTexture)
    pandaWidget.show()
   
    sys.exit(app.exec_())
rdb
pro-rsoft

Posts: 5843
Location: Netherlands

PostPosted: Wed Jun 18, 2008 2:29 am    Post subject: Reply with quote
Wow. This is so interesting! Good job! Smile
ThomasEgi


Posts: 1820
Location: Germany,Koblenz

PostPosted: Tue Aug 19, 2008 2:00 pm    Post subject: Reply with quote
digging out this thread. i just wondered if it would be possible to let a shader do the masking somehow , let it be a buffer or something. i dunno a thing about qt but it seems like a stupid conversion shaders would be fast enough for. just in case it's actually possible.
ZeroByte


Posts: 308

PostPosted: Tue Aug 19, 2008 3:24 pm    Post subject: Reply with quote
I don't know, never really tried shaders before. It would also seem to me like a conversion shader would be much more cross compatible. One of the things that I've found about the code below is the differences between how the frames are stored in the graphics card makes the code very specific to the kind of card you're running.
ThomasEgi


Posts: 1820
Location: Germany,Koblenz

PostPosted: Wed Oct 29, 2008 6:19 pm    Post subject: Reply with quote
just thought about using panda for a window-sitter.
given qt4 there might be a nice way to speed alpha-masking up. thought i dunno if its possible but it looks promising to somone who has no idea about qt (->me)
http://doc.trolltech.com/4.3/qtopengl.html
anyone experienced enough with the topic can tell me if its possible to render a transparent window on top of the screen? (without going through all-to-slow-software-conversions , and assuming a linux system , if you like with working hw-acc desktop)
thx for the brain-capacity you take to anwer the question Smile
treeform


Posts: 2027
Location: Seattle

PostPosted: Wed Oct 29, 2008 6:59 pm    Post subject: Reply with quote
no, i dont think so.
_________________
Panda3d IRC irc://irc.freenode.net/panda3d | BUGs https://bugs.launchpad.net/panda3d
My MMORTS game: http://aff2aw.com
GitHub: http://github.com/treeform | Twitter: http://twitter.com/treeform
ZeroByte


Posts: 308

PostPosted: Thu Oct 30, 2008 3:11 am    Post subject: Reply with quote
Qt has an OpenGL component that is free to use for open source projects I think. Doing a partially transparent window might be much more faster there and less hardware/OS specific. I haven't really looked into the OpenGL part of Qt though and I'm not even sure if PyQT has access to that.
ThomasEgi


Posts: 1820
Location: Germany,Koblenz

PostPosted: Thu Oct 30, 2008 9:38 am    Post subject: Reply with quote
there is a PyQt-GL binding.thought i havent found any usefull documentation on that one.
any other techniques to draw a partly transparent window ontop of the screen? video-overlays or something like that?
ZeroByte


Posts: 308

PostPosted: Thu Oct 30, 2008 10:31 am    Post subject: Reply with quote
I don't really know, I've asked before on the PyQT mailing list if there's a faster way to do semi-transparent windows and didn't really get a reply. I didn't ask about PyQT-GL though.
benchang


Posts: 103
Location: Troy, NY

PostPosted: Wed Nov 19, 2008 12:02 am    Post subject: Reply with quote
Hey, I'm really interested in this - Qt is my other recent addiction, along with Panda, so it's my two current favorite things Smile

When panda creates its main window, is there a way to parent it to another window? I don't even know what I'm talking about completely, I just gathered this from reading other peoples' Ogre+QT code. that might be more efficient than rendering to a texture and then converting it to a qimage.

Wonder if you could also use the QGLPixelBuffer for this?
dinoint


Posts: 18
Location: France

PostPosted: Mon Jan 19, 2009 8:48 am    Post subject: Reply with quote
I don't know if this wasn't available before, but I managed to embed a Panda window in QT simply by passing it the window handle of the container.

I wrote this small script with PyQT to test if the keyboard focus was working correctly as with wxPython on Ubuntu there are problems:

Code:

# -*- coding: utf-8-*-

from pandac.PandaModules import loadPrcFileData
loadPrcFileData("", "window-type none")

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import WindowProperties

from PyQt4.QtCore import *
from PyQt4.QtGui import *

import sys

P3D_WIN_WIDTH = 400
P3D_WIN_HEIGHT = 240

class QTTest(QDialog):
    def __init__(self, pandaCallback, parent=None):
        super(QDialog, self).__init__(parent)
        self.setWindowTitle("Test")
        self.setGeometry(0,0,400,300)
       
        self.pandaContainer = QWidget(self)
        self.pandaContainer.setGeometry(0,0,P3D_WIN_WIDTH,P3D_WIN_HEIGHT)

        self.lineedit = QLineEdit("Write something...does it work?")
       
        layout = QVBoxLayout()
        layout.addWidget(self.pandaContainer)
        layout.addWidget(self.lineedit)
       
        self.setLayout(layout)
       
        # this basically creates an idle task
        timer =  QTimer(self)
        self.connect( timer, SIGNAL("timeout()"), pandaCallback )
        timer.start(0)

   
class World(DirectObject):   
    def __init__(self):
        self.accept("a", self.pressedA)
        self.accept("escape", sys.exit)
   
    def pressedA(self):
        print "a pressed, keyboard focus ok"
       
    def step(self):
        taskMgr.step()
   
    def bindToWindow(self, windowHandle):
        wp = WindowProperties().getDefault()
        wp.setOrigin(0,0)
        wp.setSize(P3D_WIN_WIDTH, P3D_WIN_HEIGHT)
        wp.setParentWindow(windowHandle)
        base.openDefaultWindow(props=wp)
        self.wp = wp
       
   
if __name__ == '__main__':
    world = World()

    app = QApplication(sys.argv)
    form = QTTest(world.step)
    world.bindToWindow(int(form.winId()))
   
    form.show()
    app.exec_()



Last edited by dinoint on Thu Jan 22, 2009 12:03 pm; edited 1 time in total
zzarko


Posts: 14

PostPosted: Thu Jan 22, 2009 11:25 am    Post subject: Reply with quote
dinoint wrote:
I don't know if this wasn't available before, but I managed to embed a Panda window in QT simply by passing it the window handle of the container.

I wrote this small script with PyQT to test if the keyboard focus was working correctly as with wxPython on Ubuntu there are problems:
...


I tried it in Ubuntu and it simply works! Thanks! I also tried it in Windows, but without much success. The controls in Panda3D scene are working, but as soon as you move focus to edit control, it cannot be changed back to Panda3D panel (or I didn't found a way). Any solutions for this?
dinoint


Posts: 18
Location: France

PostPosted: Thu Jan 22, 2009 11:38 am    Post subject: Reply with quote
zzarko wrote:

I tried it in Ubuntu and it simply works! Thanks! I also tried it in Windows, but without much success. The controls in Panda3D scene are working, but as soon as you move focus to edit control, it cannot be changed back to Panda3D panel (or I didn't found a way). Any solutions for this?


On Ubuntu I just click inside the panda window in order to regain focus, on Windows I can't test, maybe try to set the focus policy on the pandaContainer widget explicitely:

Code:

self.pandaContainer.setFocusPolicy(Qt.StrongFocus)


It's just a wild guess I don't know if it'll works.
zzarko


Posts: 14

PostPosted: Thu Jan 22, 2009 11:54 am    Post subject: Reply with quote
I'll try that tonight (my machine at work is Ubuntu Smile ), thanks. BTW, for this embedding to work in Windows, bindToWindow must be called with casting:
Code:
world.bindToWindow(int(form.winId()))
dinoint


Posts: 18
Location: France

PostPosted: Thu Jan 22, 2009 12:03 pm    Post subject: Reply with quote
zzarko wrote:
I'll try that tonight (my machine at work is Ubuntu Smile ), thanks. BTW, for this embedding to work in Windows, bindToWindow must be called with casting:
Code:
world.bindToWindow(int(form.winId()))


I added your fix to the code above, thanks.
zzarko


Posts: 14

PostPosted: Fri Jan 23, 2009 2:27 am    Post subject: Reply with quote
dinoint wrote:

On Ubuntu I just click inside the panda window in order to regain focus, on Windows I can't test, maybe try to set the focus policy on the pandaContainer widget explicitely:

Code:

self.pandaContainer.setFocusPolicy(Qt.StrongFocus)


It's just a wild guess I don't know if it'll works.


Unfortunately, it didn't worked. Regaining focus is (for now) a no-no. On Ubuntu everything is working fine from the beginning. Maybe I should post the question at PyQt forum (maybe it isn't Panda-related). Anyway, thanks for trying. BTW, I'm using your code for a little project of mine (http://www.panda3d.org/phpbb2/viewtopic.php?t=5521)
zzarko


Posts: 14

PostPosted: Sun Feb 22, 2009 7:23 pm    Post subject: Reply with quote
By adding (modified version of drwr's answer for wxWidgets):
Code:
class QTPandaWidget(QWidget):
   def __init__(self, parent=None):
      super(QWidget, self).__init__(parent)
      
   def resizeEvent(self, evt):
      wp = WindowProperties()
      wp.setSize(self.width(), self.height())
      wp.setOrigin(self.x(),self.y())
      base.win.requestProperties(wp)

and replacing
Code:
self.pandaContainer = QWidget(self)

with
Code:
self.pandaContainer = QTPandaWidget(self)

you can make panda window resizeable in PyQt.

Edit: Solved positioning issues (I hope...)
Edit2: It doesn't work with QHBoxLayout, for example... Sad
zzarko


Posts: 14

PostPosted: Mon Feb 23, 2009 11:46 am    Post subject: Reply with quote
Now it works with QHBoxLayout...
Code:
class QTPandaWidget(QWidget):
   def __init__(self, parent=None):
      super(QWidget, self).__init__(parent)
      self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
      
   def resizeEvent(self, evt):
      wp = WindowProperties()
      wp.setSize(self.width(), self.height())
      wp.setOrigin(self.x(),self.y())
      base.win.requestProperties(wp)
   
   def minimumSizeHint(self):
      return QSize(400,300)
benchang


Posts: 103
Location: Troy, NY

PostPosted: Sat May 30, 2009 8:55 pm    Post subject: Reply with quote
Thanks - this is great!
benelgiac


Posts: 3

PostPosted: Sat Jul 04, 2009 4:30 am    Post subject: Reply with quote
I've been using your hints to create a Panda+PyQt-based viewer for a non-standard 3d geometry file format. However, I'm facing the problem of not being able to control the scene with mouse and keyboard, in Windows, as reported by others.

Has anyone of you ever wondered some workaround for it? I was wondering about intercepting mouse and keboard controls over the viewer widget in Qt's main loop and then passing them to Panda... what do you think about it? Does Panda provide some way to do this?
rdb
pro-rsoft

Posts: 5843
Location: Netherlands

PostPosted: Sat Jul 04, 2009 4:39 am    Post subject: Reply with quote
Dunno if this is related, but a few weeks ago someone drwr a fix to Panda CVS related to not being able to receive events correctly in embedded windows or so. So maybe it's a now-fixed Panda bug...
drwr


Posts: 8026
Location: Glendale, CA

PostPosted: Sat Jul 04, 2009 12:59 pm    Post subject: Reply with quote
Yes, it's fixed in Panda CVS now. A workaround until then is to use the Python win32 or ctypes module to call SetFocus(window) whenever you get a mouse event in the Panda window. (The problem is that Panda wasn't resetting the keyboard focus to itself.)

David
benelgiac


Posts: 3

PostPosted: Thu Oct 29, 2009 11:59 am    Post subject: Reply with quote
Good news! It's a long time I don't check the forums but I'm still using Panda so I'm happy for this.

Is the next version scheduled to be released soon? Just so I don't try to compile from source and I get a new binary the next day Very Happy

Thanks
rdb
pro-rsoft

Posts: 5843
Location: Netherlands

PostPosted: Thu Oct 29, 2009 12:45 pm    Post subject: Reply with quote
1.7.0 will be released before the end of the year. But I could email you a CVS snapshot build (contact me on my email address).
benelgiac


Posts: 3

PostPosted: Mon Mar 08, 2010 4:36 pm    Post subject: Reply with quote
Hi all again. After some time, I get to tinker again with my Panda3d/Pyqt visualizer.

rdb, thanks for your snapshot. Now I'm using the official 1.7.0 but I'm still facing the same problem of not having mouse and keyboard input on windows.

I've tried several tricks, including using ctypes to call SetFocus on the window handle obtained by GetWindow. No luck.

Is there any other thing I could try?

P.S. Unrelated, but could be useful for someone. I had to start the QTimer hooked to world.step with timer.start(1). In fact, timer.start(0) failed to produce a running panda main loop. strange.
Display posts from previous:   
Post new topic   Reply to topic    Panda3D Forum Index -> Code Snippets All times are GMT - 5 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group